User Tools

Site Tools


doc:appunti:linux:tux:mimetype

This is an old revision of the document!


Custom MIME Type and associated Application

In this article we will explain how to define a custom MIME Type with an associated application; the purpose is to automatically launch the specified application from the Firefox browser, when the user clicks over an URL pointing to a specified content type. The reference system is a GNU/Linux workstation running Debian 10.

We want to execute a local program when the user click over an URL pointing to a playlist file. The playlist is a simple text file, containing the filename of the images to be displayed along with geometry data. Geometry data allow to zoom and pan the image on the fly. The program is photo-reframe Slideshow and the playlists are saved as .m3u files.

The ultimate goal of this project is to build a GNU/Linux based kiosk, with a web based interface to select a folder containing images and start a slideshow on it.

The Content Type

A GNU/Linux box with a modern desktop environment already knows several content types; info about most of them are provided by the shared-mime-info package. The GNOME, KDE and Xfce desktop environments use that database.

You can use the xdg-mime command line tool (from the xdg-utils package) to query the database. Here it is an example against a JPEG image:

xdg-mime query filetype myphoto.jpg
image/jpeg

The xdg-mime tool uses actually a very complex procedure to determine the content type of the file. The extension of the file (glob matching) is part of this procedure, but also the actual content of the file is generally inspected (magic detection). On our system it turned out also that different binaries are invoked if you are in a console only (textual) environment or in a graphical desktop.

By defining the XDG_UTILS_DEBUG_LEVEL environment variable, you can see what binary program is actually used by xdg-mime:

XDG_UTILS_DEBUG_LEVEL=10 xdg-mime query filetype myphoto.jpg
Running gio info "/home/niccolo/myphoto.jpg"
image/jpeg

The above example is taken from a graphic environment: the program executed is actually gio.

XDG_UTILS_DEBUG_LEVEL=10 xdg-mime query filetype myphoto.jpg
Running mimetype --brief --dereference "/home/niccolo/myphoto.jpg"
image/jpeg

In the second example, executed into a text-only console, the program mimetype is executed instead. Fortunately the result is the same: the file is an image/jpeg content type.

We will create a custom MIME type/subtype to suit our needs. For the type we could use image one, to mean that each line of the playlist will point to an image file, but we prefer the application type, because we want to mean that data will require a specific application to be interpreted. For the subtype we will use the x- prefix, because the extension is not considered a standard type, so our choice is x-image-playlist.

MIME Type application/x-image-playlist

We create a playlist to experiment with; this is a playlist.m3u example:

IMG_6602.JPG|4000x2250+0+332
IMG_6605.JPG|2971x1671+796+628
IMG_6606.JPG|4000x2250+0+442
IMG_6610.JPG|3810x2143+90+387
IMG_6615.JPG|2828x1590+547+681

This is the first version of the MIME Type definition, in XML format:

<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="application/x-image-playlist">
        <comment>M3U Images Playlist</comment>
        <icon name="image"/>
        <glob pattern="*.m3u"/>
    </mime-type>
</mime-info>

We save the file as photo-reframe.xml into the /usr/local/share/mime/packages/ directory, then we update the database of MIME Types with the command:

update-mime-database /usr/local/share/mime

Several files will be created/updated in that directory, most notably: globs, globs2, magic and mime.cache.

Here we face the first problem: asking the system the type of the playlist.m3u we get audio/x-mpegurl instead of application/x-image-playlist:

xdg-mime query filetype playlist.m3u
audio/x-mpegurl

This is because the extension .m3u is already used by the MIME definition contained into the system file /usr/share/mime/packages/freedesktop.org.xml (provided by the shared-mime-info package).

We can solve the problem by choosing a different filename extension and add another <glob pattern="..."/> line into the XML definition, but here we want to show how to override the default preference order. So we add the weight="60" attribute to the glob tag, knowing that the default weight is 50 (the value must be between 0 and 100).

Executing the update-mime-database tool, the globs and globs2 files are updated. They contain the mime type and the associated extension; if globs2 exists, it is preferred because it contains the weight data, otherwise globs is used (where only the listing order matters).

WARNING! Not all the MIME query tools behave in the same way: gio (from the libglib2.0-bin package) and mimetype (from the libfile-mimeinfo-perl package) are known to return different results for the same file; one reason is that the files in /usr/local/share/mime/ and /usr/share/mime/ are not treated in the same way by the two programs.

Here we face the second problem, despite the .m3u extension is associated to our custom MIME Type with more weigth than the default audio/x-mpegurl type, xdg-mime returns the same result as above.

To get some insight we run the underlying program, which is gio (see above about the XDG_UTILS_DEBUG_LEVEL):

XDG_UTILS_DEBUG_LEVEL=10 xdg-mime query filetype playlist.m3u
Running gio info "/home/niccolo/playlist.m3u"
audio/x-mpegurl

gio info "/home/niccolo/playlist.m3u" | grep content-type
  standard::content-type: audio/x-mpegurl
  standard::fast-content-type: application/x-image-playlist

Here it is the clue: the fast-content-type is the one we want (application/x-image-playlist), but the content-type is the one which prevails. The fast-content-type is determined by just inspecting the filename extension (the glob), but the more important content-type is determined by inspecting the actual content of the file (the magic).

There is no choiche: we need to add some info to let the magic work. Taking a cue from the Extended M3U, we will use the pseudo comment #EXTM3U-PHOTO-REFRAME as the first line of the playlist.

So this is the final version of th XML file to define the MIME type:

<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="application/x-image-playlist">
        <comment>M3U Images Playlist</comment>
        <icon name="image"/>
        <magic priority="60">
            <match offset="0" type="string" value="#EXTM3U-PHOTO-REFRAME"/>
        </magic>
        <glob pattern="*.m3u" weight="60"/>
    </mime-type>
</mime-info>

The <magic> tag uses the match method: the file will be inspected starting at offset zero, searching for that magic string. The priority value of 60, assures that this match will prevail over the defaults. To update the MIME database another run of update-mime-database is required.

This is the new version of the playlist:

#EXTM3U-PHOTO-REFRAME
IMG_6602.JPG|4000x2250+0+332
IMG_6605.JPG|2971x1671+796+628
IMG_6606.JPG|4000x2250+0+442
IMG_6610.JPG|3810x2143+90+387
IMG_6615.JPG|2828x1590+547+681

Finally the MIME type detection works as expected:

xdg-mime query filetype playlist.m3u
application/x-image-playlist

Configuration system-wide or user-wide

The MIME related tools search for the MIME database in several directories. On a Debian system there is the directory where offical packages install their definitions, the directory where the system administrator can install local configuration valid for all the users, and the user's personal directory:

  • /usr/share/mime/
  • /usr/local/share/mime/
  • $HOME/.local/share/mime/

Application associated to a MIME Type

To view if there is an application associated with our custom MIME Type, we can run the following command:

xdg-mime query default application/x-image-playlist

No result is returned, as we can expect.

Web References

doc/appunti/linux/tux/mimetype.1600018954.txt.gz · Last modified: 2020/09/13 19:42 by niccolo