Java Minimal Template Engine

tinyMediaManager uses the Java Minimal Template Engine (JMTE) to perform “object to text” transformations which are used in

  • the renamer (movies & TV shows)
  • export templates

JMTE has a flexible syntax to achieve almost everything you like. The language is really simple and can be learned in no time. It is all about having a text with small island of expressions that expand to text or control the expansion of text fragments. Those expressions are enclosed in special characters which are freely configurable but are by default

  • ${ for the start of an expression and
  • } for its end

Replacing variables

The simplest form of an expression is to expand an entry contained in the model to a string: ${title}

If your model contains complex objects and you want to expand a certain property or field you can use the “.” notation to navigate to it like e.g. ${movie.title}.

Renderer information

How a variable is eventually converted to a string is determined by renderers. There are renderers which are automatically called based on the type of the variable. However, there are also so called “named renderers” which you can address by passing their name along with the name of the variable. You do this by adding a semicolon and the name of the renderer to the variable name. You can even pass format information in brackets.

For example this

${movie.releaseDate;date(yyyy-MM-dd)}

would cause the variable called movie.releaseDate to be formatted with a renderer named date. This named renderer would then be passed the format yyyy-MM-dd along with the variable to be rendered. Named renderers know which types they support and are responsible to do the necessary conversion for them.

Conditional expansion

Sometimes you might want to display certain text or expand certain expression only when certain conditions hold. The minimal template engine provides you with an if-statement to do so.

${if condition}
  text displayed if condition holds
${else}
  text displayed if condition does not hold
${end}

The else-part is optional and the whole conditional block has to be terminated by the end-expression. If-expressions can be nested and conditions can be negated using the “!”-operator. You can also compare to a string constant using the “=”-operator.

${if ! media.type = "VIDEO"}
  text displayed if media.type is NOT "VIDEO"
${else}
  text displayed if media.type is "VIDEO"
${end}

There are no other logical operators, i.e. you can not combine conditions using and or or. The reason is that this is a minimal language, expressions can be complex and conditions can be constructed in the Java model. You can either have a condition directly inside the model or you can build a processor that computes it and add it to the model.

The condition can be any object. It evaluates using these rules

  • if the object is null or calling toString on the object returns the empty string or false the condition evaluates to false
  • if the object is a boolean, the condition evaluates to the value of that boolean
  • if the object is a map, a collection, an array or any iterable the condition evaluates to true if they contain any elements
  • otherwise the condition evaluates to true

Conditional shortcuts

For convenience reasons there are some shortcuts for the most commonly used conditional statements. The first shortcut works by supplying a default constant value which will be used in case the variable is undefined. You can specify such a default value in brackets. E.g.

${movie.title(unknown)}

would be equivalent to

${if movie.title}
  ${movie.title}
${else}
  unknown
${end}

The second shortcut works by wrapping a value, but only if it is defined. You can wrap a value by adding a prefix and a suffix separated by commas. This means

${<h1>,movie.title,</h1>}

is essentially the same as

${if movie.title}
  <h1>${movie.title}</h1>
${end}

To avoid ambiguities, both prefix and suffix must be supplied or none at all, but they can also be the empty string.

Loops (expanding more than one item)

If you want to expand all elements of a container you can use the foreach-expression.

${foreach container item}
  ${item}
${end}

In this example variable item is set to each element of the container and the body - i.e. the part between the foreach- and the end-expression - is expanded for each item in the container. The container can be anything that implements java.lang.Iterable or anything that implements java.util.Map or any array. If the container is a map we iterate over its entry set and the items will be of type Map.Entry. This means you can access its parts using ${item.key} and ${item.value}.

Loops can be nested and can also contain all kinds of other expressions. You are not able to specify a step or a start of end index.

Special variables

In certain cases you might want to change the way an item in a container shall be displayed depending on its position inside the container. One example is to have the rows in a table be displayed having an alternating format depending whether their row number is even or odd.

Current list of special variables

  • first
  • last
  • odd
  • even
  • index

Special variables are suffixed by a _ followed by the name of the item variable. This means inside a foreach loop that has item as the item name there will be the variable first_item.

Example:

${foreach items item}
  ${if first_item}
    <em>${item}</em>
  ${else}
    ${item}
  ${end}
${end}

Separators

When displaying a list of items you might want to have a separator between each item. This will not work using a normal body as there shall be no additional separator after the last item. You can either solve this using special variables - explained in the previous section - or more easily using a separator. Such a separator is added into the foreach expression like this

${foreach container item , }${item}${end}

Suppose the container contains the items item1 and item2 then the formatted output looks like item1, item2.

Escaping

When you want to use the special character sequences literally you will have to escape them using a the backslash \. You can also escape the backslash by adding a second. In Java this would be

  • \${
  • \}
  • \\

and if you read your template from a file

  • ${
  • }
  • \

Regular expression annotation

You can use this regular expression annotation to extract/modify parts of the chosen token. The syntax of the annotation is as follows:

${@regexp expression source destination}

where

  • ${@regexp - opens the annotation for JMTE
  • expression - is the regular expression. NOTE: you cannot use {} in the regular expression since those will conflict with JMTE
  • source - the source token where to take the information from. ATTENTION: you need to insert the whole token (and not the short token). E.g. movie.originalFilename
  • destination - a string with placeholders where the found parts of the regular expression will be entered. $1 will use the findings from group 1, $2will use findings from group 2, …

JMTE uses the Java syntax for regular expression. You can test your regular expression with the online tool RegexPlanet.

Available data

In addition to the shortcut tokens of the movie renamer and TV show renamer you are able to access most internal data structures by directly addressing them. Foe example if you want to address the bitrate of the first audio stream of the main video file in a movie, you have to navigate the following way with JMTE syntax:

${movie.mainVideoFile.audioStreams[0].bitrate}

The following list contains an automatic generated export of all available fields in the entities used for movies and TV shows:

Movies

Movies  
List<Person> actors
Map<String,MediaFile> artworkMap
Map<String,String> artworkUrls
String certification
String country
String dataSource
Date dateAdded
String dateAddedAsString
Date dateAddedForUi
UUID dbId
String decadeLong
String decadeShort
List<Person> directors
String directorsAsString
boolean disc
boolean duplicate
String edition
String editionAsString
List<String> extraFanarts
List<String> extraThumbs
List<String> genres
String genresAsString
Boolean hasImages
Boolean hasMetadata
Boolean hasNfoFile
Boolean hasNote
boolean hasRating
boolean hasSubtitles
Boolean hasTrailer
Map<String,Object> ids
String imdbId
String lastScrapeLanguage
String lastScraperId
Date lastWatched
String localizedSpokenLanguages
MediaFile mainVideoFile
List<MediaFile> mediaFiles
List<MediaFile> mediaFilesContainingAudioStreams
List<MediaFile> mediaFilesContainingSubtitles
float mediaInfoAspectRatio
Float mediaInfoAspectRatio2
String mediaInfoAspectRatio2AsString
String mediaInfoAspectRatioAsString
List<String> mediaInfoAudioChannelList
String mediaInfoAudioChannels
String mediaInfoAudioCodec
String mediaInfoAudioCodecAndChannels
List<String> mediaInfoAudioCodecList
String mediaInfoAudioLanguage
List<String> mediaInfoAudioLanguageList
String mediaInfoContainerFormat
double mediaInfoFrameRate
String mediaInfoSource
List<String> mediaInfoSubtitleLanguageList
int mediaInfoVideoBitDepth
int mediaInfoVideoBitrate
String mediaInfoVideoCodec
String mediaInfoVideoFormat
String mediaInfoVideoResolution
String mediaSource
MovieSet movieSet
String movieSetTitle
boolean multiMovieDir
String note
boolean offline
String originalFilename
String originalTitle
String originalTitleSortable
String parent
String path
int playcount
String plot
List<Person> producers
String productionCompany
List<String> productionCompanyAsArray
MediaRating rating
Map<String,MediaRating> ratings
Date releaseDate
String releaseDateAsString
String releaseDateFormatted
int runtime
int runtimeFromMediaFiles
int runtimeFromMediaFilesInMinutes
boolean scraped
List<String> showlinks
String showlinksAsString
String sortTitle
String spokenLanguages
boolean stacked
String tagline
List<String> tags
String tagsAsString
String title
String titleForUi
String titleSortable
int tmdbId
int top250
List<MediaTrailer> trailer
String video3DFormat
String videoBasenameWithoutStacking
List<MediaFile> videoFiles
long videoFilesize
String videoHDR
String videoHDRFormat
boolean videoIn3D
boolean watched
List<Person> writers
String writersAsString
int year
MovieSet  
Map<String,MediaFile> artworkMap
Map<String,String> artworkUrls
Date dateAdded
String dateAddedAsString
Date dateAddedForUi
UUID dbId
List<Movie> dummyMovies
Boolean hasImages
Boolean hasMetadata
Map<String,Object> ids
List<MediaFile> mediaFiles
List<Movie> movies
String note
String originalTitle
String plot
String title
String titleSortable
int tmdbId
String years

TV Shows

TvShow  
List<Person> actors
Map<String,MediaFile> artworkMap
Map<String,String> artworkUrls
String certification
String country
String dataSource
Date dateAdded
String dateAddedAsString
Date dateAddedForUi
UUID dbId
String decadeLong
String decadeShort
int dummyEpisodeCount
List<TvShowEpisode> dummyEpisodes
boolean duplicate
int episodeCount
List<TvShowEpisode> episodes
List<TvShowEpisode> episodesForDisplay
List<MediaFile> episodesMediaFiles
List<TvShowEpisode> episodesToScrape
List<String> extraFanartUrls
Date firstAired
String firstAiredAsString
String firstAiredFormatted
List<String> genres
String genresAsString
Boolean hasEpisodeMetadata
Boolean hasMusicTheme
Boolean hasNfoFile
Boolean hasNote
Boolean hasSeasonAndEpisodeImages
Boolean hasTrailer
Map<String,Object> ids
String imdbId
String lastScrapeLanguage
String lastScraperId
Date lastWatched
MediaFile mainVideoFile
List<MediaFile> mediaFiles
float mediaInfoAspectRatio
Float mediaInfoAspectRatio2
List<String> mediaInfoAudioChannelList
String mediaInfoAudioChannels
String mediaInfoAudioCodec
List<String> mediaInfoAudioCodecList
String mediaInfoAudioLanguage
List<String> mediaInfoAudioLanguageList
String mediaInfoContainerFormat
double mediaInfoFrameRate
String mediaInfoSource
List<String> mediaInfoSubtitleLanguageList
int mediaInfoVideoBitDepth
String mediaInfoVideoCodec
String mediaInfoVideoFormat
String mediaInfoVideoResolution
String note
String originalTitle
String path
String plot
String productionCompany
List<String> productionCompanyAsArray
MediaRating rating
Map<String,MediaRating> ratings
int runtime
boolean scraped
int seasonCount
List<TvShowSeason> seasons
String sortTitle
String status
List<String> tags
String tagsAsString
String title
String titleSortable
List<MediaTrailer> trailer
int traktId
String tvdbId
MediaRating userRating
String videoHDRFormat
boolean videoIn3D
boolean watched
int year
TvShowSeason  
boolean dummy
List<TvShowEpisode> episodes
List<TvShowEpisode> episodesForDisplay
Date firstAired
Boolean hasEpisodeImages
Boolean hasEpisodeNfoFiles
Date lastWatched
List<MediaFile> mediaFiles
int season
String title
TvShow tvShow
boolean watched
TvShowEpisode  
List<Person> actors
int airedEpisode
int airedSeason
Map<String,MediaFile> artworkMap
Map<String,String> artworkUrls
String certification
String dataSource
Date dateAdded
String dateAddedAsString
Date dateAddedForUi
UUID dbId
List<Person> directors
String directorsAsString
boolean disc
int displayEpisode
int displaySeason
boolean dummy
int dvdEpisode
boolean dvdOrder
int dvdSeason
int episode
Date firstAired
String firstAiredAsString
String firstAiredFormatted
boolean hasNote
boolean hasSubtitles
Map<String,Object> ids
String imdbId
Date lastWatched
MediaFile mainVideoFile
List<MediaFile> mediaFiles
List<MediaFile> mediaFilesContainingAudioStreams
List<MediaFile> mediaFilesContainingSubtitles
float mediaInfoAspectRatio
Float mediaInfoAspectRatio2
String mediaInfoAspectRatio2AsString
String mediaInfoAspectRatioAsString
List<String> mediaInfoAudioChannelList
String mediaInfoAudioChannels
String mediaInfoAudioCodec
String mediaInfoAudioCodecAndChannels
List<String> mediaInfoAudioCodecList
String mediaInfoAudioLanguage
List<String> mediaInfoAudioLanguageList
String mediaInfoContainerFormat
double mediaInfoFrameRate
String mediaInfoSource
int mediaInfoVideoBitDepth
int mediaInfoVideoBitrate
String mediaInfoVideoCodec
String mediaInfoVideoFormat
String mediaInfoVideoResolution
String mediaSource
boolean multiEpisode
String note
String originalFilename
String originalTitle
String parent
String path
Path pathNIO
String plot
String productionCompany
List productionCompanyAsArray
MediaRating rating
Map<String,MediaRating> ratings
int runtime
int runtimeFromMediaFiles
int runtimeFromMediaFilesInMinutes
boolean scraped
int season
boolean stacked
List<String> tags
String tagsAsString
String title
String titleForUi
String titleSortable
TvShow tvShow
UUID tvShowDbId
TvShowSeason tvShowSeason
String tvdbId
String videoBasenameWithoutStacking
List<MediaFile> videoFiles
long videoFilesize
String videoHDRFormat
boolean videoIn3D
boolean watched
List<Person> writers
String writersAsString
int year

Common Entities

Person  
String name
String nameForStorage
String profileUrl
String role
String thumbUrl
String type
MediaRating  
String id
int maxValue
float rating
float ratingNormalized
int votes
MediaFile  
boolean DVDFile
boolean HDDVDFile
boolean HDR
boolean ISO
boolean animatedGraphic
float aspectRatio
Float aspectRatio2
int audioChannelCount
String audioChannels
List<String> audioChannelsList
String audioCodec
List<String> audioCodecList
String audioLanguage
List<String> audioLanguagesList
List<MediaFileAudioStream> audioStreams
List audioTitleList
String basename
int bitDepth
String bitDepthString
boolean blurayFile
String combinedCodecs
String containerFormat
Date dateCreated
Date dateLastModified
MediaFileAudioStream defaultOrBestAudioStream
boolean discFile
int duration
String durationHHMMSS
String durationHM
String durationHMS
int durationInMinutes
String durationShort
String exactVideoFormat
String extension
Path file
Path fileAsPath
long filedate
String filename
String filenameWithoutStacking
long filesize
String filesizeInMegabytes
double frameRate
boolean graphic
boolean hdDVDFile
String hdrFormat
boolean mainDiscIdentifierFile
boolean musicTheme
int overallBitRate
String overallBitRateInKbps
boolean packed
String path
int stacking
String stackingMarker
List<String> subtitleLanguagesList
List<MediaFileSubtitle> subtitles
String subtitlesAsString
boolean textual
String title
String type
boolean video
String video3DFormat
int videoBitRate
String videoBitRateInKbps
String videoCodec
String videoDefinitionCategory
boolean videoDefinitionHD
boolean videoDefinitionLD
boolean videoDefinitionSD
boolean videoDefinitionUHD
String videoFormat
int videoHeight
String videoResolution
int videoWidth
MediaFileAudioStream  
int audioChannels
String audioTitle
int bitrate
String bitrateInKbps
String codec
boolean defaultStream
boolean forced
String language
MediaFileSubtitle  
String codec
boolean defaultStream
boolean forced
String language
MediaFileType  
VIDEO main video files
TRAILER trailer
SAMPLE sample != trailer
AUDIO audio files
SUBTITLE subtitle files
NFO NFO files
POSTER poster
FANART fanart
BANNER banner
CLEARART clearart
DISCART disc art
LOGO logo
CLEARLOGO clear logo
THUMB thumb
CHARACTERART character art
KEYART key art
SEASON_POSTER season poster
SEASON_BANNER season banner
SEASON_THUMB season thumb
EXTRAFANART extra fanart
EXTRATHUMB extra thumb
EXTRA any extra file
GRAPHIC generic graphic
MEDIAINFO xxx-mediainfo.xml
VSMETA xxx.ext.vsmeta Synology
THEME “theme” files for some skins, like theme.mp3 (or bg video)
TEXT various text infos, like BDinfo.txt or others…
UNKNOWN  
MediaTrailer  
String date
Boolean inNfo
String name
String provider
String quality
String url