Minstrel

The FLOSS hybrid reading app

Built together by readers, for the readers

Developer Information

This page contains information useful for developers and book producers which might be interested in extending/working with Minstrel app.

If you are looking for help using Menestrello, the official built of the app from ReadBeyond, please consult the Menestrello Help page instead.

App Overview

  • Hybrid app compiled using Apache Cordova 5.0.0
  • Platforms: Android (4.0+) and iOS (6.0+)
  • Minstrel Version: 3.0.1 (date 2015-05-20, build 7fa72ad0)
  • Menestrello Version: 3.0.0 (date 2015-02-05, build 380f1dbb)

Library

iOS

Minstrel will read ABZ, CBZ, and EPUB files from the app Documents/ directory and its subdirectories. The user can copy files there:

  1. using iTunes, or
  2. downloading files from the Web using Safari, and selecting Open with Minstrel.

Android

Minstrel can open ABZ, CBZ, and EPUB files by:

  1. scanning one or more directories on the device storage (both internal memory or external sd cards) selected by the user,
  2. responding to a VIEW intent from the Download Manager/Notification Center or a file browser, or
  3. responding to a VIEW intent from the browser (directly downloading the requested file).

If the user presses the Refresh button, the app will refresh the library by scanning the directories selected by the user as containing the publications.

You can change these directories by pressing the button Change library directory in Settings > Library.

To improve the speed of the scanning process, you might want to copy your eBooks into a single directory (possibly, with subdirectories), and select that directory only.

Cover thumbnail images are stored in the /INTERNALSD/minstrel/.thumbnails/ directory, which can be safely deleted if you uninstalled the app and need space on your device.

The book assets are extracted into a /INTERNALSD/minstrel/tmp/ directory. This directory is emptied as soon as a book is closed and the user returns to the Library View. You can safely delete the tmp directory if need space on your device.

Important note: by default, Minstrel uses the /INTERNALSD/minstrel/ directory as the main app directory, instead of using the standard /data/Android/app/ path, since the latter directory might be inacessible to the user on some devices, and we think that the user should be able to control the app behavior, including the temporary data it writes.

Localization

Localization of the UI strings is managed by a custom JS library, able to load both JS-like files and PO files.

At the moment, the 8 available languages (DA, DE, EN, ES, FR, IT, PL, TR) are loaded from JS files located in the app package, but a future version of the app will load PO files from the user storage, allowing loading a new language by simply providing a user-created PO file.

Note: for DA, DE, ES, FR, PL, TR languages, many strings added in v3.0.0 have been translated automatically. If you would like to clean them up, or you want to translate Minstrel in a new language, please contact us (minstrel@readbeyond.it). We will be very happy to include your translation and credit you for your work.

EPUB reader

Rendering strategy

When the user opens an EPUB in the Library, the EPUB is unzipped into a temporary directory. A JS library parses the OPF and related files, and builds the data structures needed to render it (reading order, ToC, playlist, etc.).

Audio files associated with Media Overlays are unzipped "on demand", to reduce both the startup time and the disk consumption for the temporary directory. To reduce the transition time from a chapter/track to the next one, a preloading mechanism is in place. Note that every audio file required by the SMIL file referenced by an XHTML file is unzipped before the XHTML is rendered.

Once all required assets have been extracted to the temporary directory, the app will load the source of the XHTML page into a special <div>. Currently, <iframe> is not used due to several issues related to Cordova, but future versions of the app might use it.

Note that the CSS files and <style> elements of the EPUB will be applied together with the CSS rules derived from the user preferences expressed through the app controls. (See also below.)

Media Overlays limitations

Currently, most of the EPUB 3 Media Overlays specification is supported, except:

  1. skippability/escapability functions, and
  2. embedded media synchronization.

Moreover, epub:type attributes are ignored.

As of v3.0.0, SMIL files referencing more XHTML or audio files are supported, as well as non-contiguous (clipBegin(i+1) != clipEnd(i)) sequences.

Minstrel also supports the proposed <meta property="media:paused-class">pausedClass</meta> meta, which applies pausedClass to the current MO element, if the playback is paused, similarly to <meta property="media:active-class">.

Pre-paginated (Fixed Layout) limitations

Support for pre-paginated EPUB 3 (and hybrid EPUB 3) is very experimental. Currently only single-page rendering is supported.

JavaScript limitations

Support for running embedded JavaScript is very experimental.

User must explicitly enable this function in "Settings > EPUB > Advanced". When disabled, all <script> elements are removed.

When enabled, all <script> elements will be injected, however only the <body onload="..."> function is run automatically on page load. Other functions (e.g., attached to element events) are run when needed.

The navigator.epubReadingSystem object is exposed, with name Minstrel. Beyond exposing the properties/functions mandated by the EPUB 3 specification, the navigator.epubReadingSystem object (rs for short below) offered by Minstrel also provides the following:

  • rs.events.onApplyHighlightStyleChange(newValue) event
  • rs.events.onApplyTypographicSettingsChange(newValue) event
  • rs.events.onAutoScrollChange(newValue) event
  • rs.events.onBackgroundColorChange(newValue) event
  • rs.events.onFontColorChange(newValue) event
  • rs.events.onFontFamilyChange(newValue) event
  • rs.events.onFontSizeChange(newValue) event
  • rs.events.onHighlightStyleChange(newValue) event
  • rs.events.onLineSpacingChange(newValue) event
  • rs.events.onMarginSizeChange(newValue) event
  • rs.events.onMarginSizeChange(newValue) event
  • rs.events.onOrientationChange(orientation, isOrientationLocked) event
  • rs.events.onPlaybackSpeedChange(newValue) event
  • rs.events.onPlaybackVolumeChange(newValue) event
  • rs.events.onTapToPlayChange(newValue) event
  • rs.events.onTextAlignChange(newValue) event
  • rs.events.onTextTransformChange(newValue) event
  • rs.settings.injectUserCSS property (values: true, false)
  • rs.settings.stripPublisherCSS property (values: true, false)
  • rs.ui.brightness property (value: float between 0.0 and 1.0)
  • rs.ui.language property (value: 2-letters ISO code for the UI language)
  • rs.ui.orientation property (values: portrait, landscape, reversed portrait, reverse landscape)
  • rs.ui.orientationLock property (values: true, false)
  • rs.ui.theme property (values: light or dark)

Please see this demo EPUB for an usage example.

Note: the above API is extremely experimental and it might change in the next version of the app.

Linear="no" options

In "Settings > EPUB > Advanced", the user can select whether spine items with linear="no" should be rendered in the linear progression of the EPUB assets, and whether an element with linear="no" should be considered as a cul-de-sac, that is, whether transitioning out of it should be possible only via the ToC or a link.

Moreover, if the <metadata> element of the EPUB OPF file contains a <meta name="nonlinear:crossable" content="false"/> element, the EPUB viewer will treat all linear="no" spine items as culs-de-sac.

CSS Overriding/Custom CSS

When the user enables the "Ignore publisher's CSS" option, all <style> elements and linked CSS will be removed.

When the user enables the "Use custom CSS" option, the app will look for a custom.css (or custom.night.css if the night mode is active) CSS file in the app root directory (e.g., /sdcard/minstrel/custom.css in Android) and it will apply it to rendered EPUB files.

Note that the above two settings are independent, and might conflict and/or complement each other. Moreover, they are both independent from the EPUB Reader preferences (font face, size, etc.).

ReadBeyond customizations

When this "Settings > EPUB > Advanced" option is enabled, it will perform the following customizations:

  • resize the ebook cover image to fill the viewport (an heuristic is applied to "recognize" the cover spine item)
  • show the Playlist popup instead of rendering playlist.xhtml
  • remove the header <div> containing the <audio> element and navigation links in ReadBeyond EPUB 3 Audio-eBooks

EPUB files from third party should be unaffected by this option. However, you can always switch it off in "Settings > EPUB > Advanced".

CBZ reader

Minstrel can display CBZ files, that is, ZIP of image files.

The rendering order is given by the lexicographic order of the ZIP entries. For example:

001 foo.jpg
002 bar.jpg
003 baz.png
...
010 foo.jpg
011 bar baz.jpg
...

Minstrel supports nested subdirectories, taking the full path into account:

foo/bar/01/01 foo.jpg
foo/bar/01/02 bar.jpg
foo/bar/01/03 baz.png
foo/bar/02/01.png
foo/bar/02/02.jpg
foo/bar/02/03.jpg
foo/bar/02/04.gif
...

If a plain text metadata.txt file exists inside the CBZ file, Minstrel will try to read the publication metadata from it. Example:

title: Bar Baz
author: Foo Foo
language: en
cover: path/to/my/cover.jpg
playlist: path/to/playlist.txt
series: Series name
seriesindex: 1

All fields are optional.

The cover field specifies the path (relative to metadata.txt) of the cover image. If none is specified, the app will look for cover.jpg or cover.png. If none is found, the first image of the CBZ will be used as cover image.

The playlist field specifies the path (relative to metadata.txt) of the playlist file, which might override the default lexicographic order:

album1/01.png
5
Caption of image 01.png

album1/13.png
5
Caption of second image

album2/04.png
10
Caption of third image

For each image, the first row specifies the image path (relative to the playlist.txt file), the second row an image duration (in seconds), and the third an image caption. The image duration and caption are used in presentation mode, and they are optional. Each image block is terminated by a blank line.

ABZ reader

Minstrel can play ABZ files, that is, ZIP of audio files.

The playing order is given by the lexicographic order of the ZIP entries. For example:

001 title.mp3
002 another title.mp3
003 foo.mp3
...
010 bar.mp3
011 baz.mp3
...

Minstrel supports nested subdirectories, taking the full path into account:

foo/bar/01/01.mp3
foo/bar/01/02.wav
foo/bar/01/03.mp4
foo/bar/02/01.aac
foo/bar/02/02.mp3
foo/bar/02/03.mp3
foo/bar/02/04.mp3
...

If a plain text metadata.txt file exists inside the ABZ file, Minstrel will try to read the publication metadata from it. Example:

title: Bar Baz
author: Foo Foo
narrator: Nar Rator
language: en
cover: path/to/my/cover.jpg
playlist: path/to/playlist.m3u
duration: 01:23:45
series: Series name
seriesindex: 3

All fields are optional.

The cover field specifies the path (relative to metadata.txt) of the cover image. If none is specified, the app will look for a file named cover.jpg or cover.png.

The playlist field specifies the path (relative to metadata.txt) of an M3U playlist. If none is specified, the app will look for a file named playlist.m3u. If none is found, the app will play the audio files according to the lexicographic order of the ZIP entry names.

Currently, the app does not extract metadata/cover image from the audio files (e.g., ID3 tags from MP3 files). A future version of the app might do so.

Supporting new file formats

As of v3.0.0, Minstrel code has been rewritten to make adding a new file format extremely simple.

In practical terms, this means developing:

  1. a native (= Java, ObjC) "discovery" function that inspects a given file on disk, and returns whether it is supported and, if so, extracts its metadata for the Library View
  2. a "reader" HTML+JS page; the app common JS libs offer support for functions like zipping/unzipping/loading settings, etc.
  3. optionally, a user-controllable "settings" HTML+JS page

For instance, in our experimental branches (i.e., not available in the public app yet) we have a PDF reader (powered by PDF.js) and a Markdown reader/writer (powered by marked.js).

Comments and bug reports