Difference between revisions of "MrC-temp"

From JRiverWiki
Jump to: navigation, search
(more live updates; spelling corrections)
(more live updates)
Line 19: Line 19:
  
 
Media Center provides a simple programming language that enhances and enriches its overall user interface and usability.
 
Media Center provides a simple programming language that enhances and enriches its overall user interface and usability.
This language, commonly called the expression language is simple to learn, simple to use, and can greatly enhance your experience using Media Center.
+
This language, commonly called the expression language, is simple to learn, simple to use, and can greatly enhance your experience using Media Center.
  
 
Expressions are ubiquitous throughout Media Center, used in areas such as:
 
Expressions are ubiquitous throughout Media Center, used in areas such as:
Line 68: Line 68:
 
If an argument is optional, it can be omitted and its default value will be used.
 
If an argument is optional, it can be omitted and its default value will be used.
 
If the argument is omitted, a comma-separator will still be required if additional arguments follow.
 
If the argument is omitted, a comma-separator will still be required if additional arguments follow.
Here is an example using the FixCase() function to change its input to Title Case:  
+
The following example uses the [[#FixCase|FixCase()]] function to change its input to Title Case:  
  
 
<div style="margin-left: 20pt"><span style="font-family: Consolas, monospace;">fixcase(A good movie)</span></div>
 
<div style="margin-left: 20pt"><span style="font-family: Consolas, monospace;">fixcase(A good movie)</span></div>
  
which outputs <span style="font-family: Consolas, monospace;">A Good Movie</span>.
+
The result is <span style="font-family: Consolas, monospace;">A Good Movie</span>.
  
 
A slightly more complex expression example consists of both text and a nested function call:
 
A slightly more complex expression example consists of both text and a nested function call:
Line 78: Line 78:
 
:<span style="font-family: Consolas, monospace;">Wow! fixcase(replace(A good movie, good, great))</span>
 
:<span style="font-family: Consolas, monospace;">Wow! fixcase(replace(A good movie, good, great))</span>
  
Inner functions are called before outer functions, so the output of the [[#Replace|Replace()]] function
+
Inner functions are called before outer functions, so the [[#Replace|Replace()]] function is call first:
  
 
<div style="margin-left: 20pt"><span style="font-family: Consolas, monospace;">replace(A good movie, good, great)</span></div>
 
<div style="margin-left: 20pt"><span style="font-family: Consolas, monospace;">replace(A good movie, good, great)</span></div>
  
is then supplied as the input to the [[#FixCase|FixCase()]] function. [[#Replace|Replace()]] does its work substituting <span style="font-family: Consolas, monospace;">good</span> with <span style="font-family: Consolas, monospace;">great</span>, and returns <span style="font-family: Consolas, monospace;">A great movie</span>.   
+
and its output is then supplied as the input to the [[#FixCase|FixCase()]] function.
This output is then supplied as the argument to [[#FixCase|FixCase()]] which sees only the text <span style="font-family: Consolas, monospace;">A great movie</span> (it knows nothing about how it was produced):
+
[[#Replace|Replace()]] does its work substituting <span style="font-family: Consolas, monospace;">good</span> with <span style="font-family: Consolas, monospace;">great</span>, and returns <span style="font-family: Consolas, monospace;">A great movie</span>.   
 +
This output is then supplied as the argument to [[#FixCase|FixCase()]] which sees only the text <span style="font-family: Consolas, monospace;">A great movie</span> (it knows nothing about how it was produced).  So the function call:
  
 
<div style="margin-left: 20pt"><span style="font-family: Consolas, monospace;">fixcase(A great movie)</span></div>
 
<div style="margin-left: 20pt"><span style="font-family: Consolas, monospace;">fixcase(A great movie)</span></div>
  
which in turn outputs <span style="font-family: Consolas, monospace;">A Great Movie</span>.
+
in turn outputs <span style="font-family: Consolas, monospace;">A Great Movie</span>.
 
Now that the functions have produced their output, the final output, including the literal <span style="font-family: Consolas, monospace;">Wow! </span> leading text is
 
Now that the functions have produced their output, the final output, including the literal <span style="font-family: Consolas, monospace;">Wow! </span> leading text is
  
Line 96: Line 97:
 
Expressions have much more utility when they use data from other sources, such as a file's metadata.
 
Expressions have much more utility when they use data from other sources, such as a file's metadata.
 
Media Center maintains this metadata in its defined fields.
 
Media Center maintains this metadata in its defined fields.
This data is accessed using the function called Field(), and its first argument is the case-insensitive name of the desired field.
+
This data is accessed using the [[#Field|Field()]] function, and its first argument is the case-insensitive name of the field to be accessed.
This function when called returns the file's value for that field.
+
For example, the function call <span style="font-family: Consolas, monospace;">field(album)</span> will return the current* file's value for the album field (* more will be said later about the current file).
For example, the function call Field(album) will return the file's value for the album field.
 
 
If the album field contained the value <span style="font-family: Consolas, monospace;">After Hours</span>, the expression:
 
If the album field contained the value <span style="font-family: Consolas, monospace;">After Hours</span>, the expression:
  
Line 105: Line 105:
 
would produce <span style="font-family: Consolas, monospace;">AFTER HOURS</span>.   
 
would produce <span style="font-family: Consolas, monospace;">AFTER HOURS</span>.   
 
First <span style="font-family: Consolas, monospace;">field(album)</span> is evaluated, returning <span style="font-family: Consolas, monospace;">After Hours</span>.
 
First <span style="font-family: Consolas, monospace;">field(album)</span> is evaluated, returning <span style="font-family: Consolas, monospace;">After Hours</span>.
The FixCase() function is supplied with this output as its first argument, and its second argument is <span style="font-family: Consolas, monospace;">3</span>, which happens to specify that it should perform uppercasing.
+
The [[#FixCase|FixCase()]] function is supplied with this output as its first argument, and its second argument is <span style="font-family: Consolas, monospace;">3</span>, which happens to specify that it should perform upper-casing.
  
 
Because fields are so frequently used in expressions, an abbreviated form exists for accessing their values. This makes it easier to both read and write expressions.
 
Because fields are so frequently used in expressions, an abbreviated form exists for accessing their values. This makes it easier to both read and write expressions.
 
Nonetheless, both forms are equivalent.
 
Nonetheless, both forms are equivalent.
The abbreviated form is simple: immediately surround the field's name with opening and closing square brackets, such as <span style="font-family: Consolas, monospace;">[album]</span>.
+
The abbreviated form is simple: immediately surround the field's name with opening and closing square brackets, for example, <span style="font-family: Consolas, monospace;">[album]</span>.
 
The previous example is now written more simply as:
 
The previous example is now written more simply as:
  
Line 205: Line 205:
  
 
====A Complex Expression Example====
 
====A Complex Expression Example====
 +
[[File:Expression_Editor.png|right]]
 
Here is a more complex expression example that illustrates the various rules discussed above regarding expressions:
 
Here is a more complex expression example that illustrates the various rules discussed above regarding expressions:
  
Line 320: Line 321:
 
The values within a List type are split into their individual (semicolon-separated) list items
 
The values within a List type are split into their individual (semicolon-separated) list items
 
The backslash character takes on a special meaning and becomes another form of separator that creates tree-like hierarchies,
 
The backslash character takes on a special meaning and becomes another form of separator that creates tree-like hierarchies,
collapsable in panes columns and creates drill-down categories in any category view type (Standard View > Categories, Theater View, DLNA, Gizmo/WebGizmo).
+
collapsible in panes columns and creates drill-down categories in any category view type (Standard View > Categories, Theater View, DLNA, Gizmo/WebGizmo).
 
Forcing an expression's type to <span style="font-family: Consolas, monospace;">list</span> causes this list item separation and hierarchy generation.
 
Forcing an expression's type to <span style="font-family: Consolas, monospace;">list</span> causes this list item separation and hierarchy generation.
 
Alternatively, forcing a List type to <span style="font-family: Consolas, monospace;">string</span> defeats this.
 
Alternatively, forcing a List type to <span style="font-family: Consolas, monospace;">string</span> defeats this.

Revision as of 15:22, 23 August 2013

This is MrC's scratch space for work-in-progress Wiki pages.

Note: The Expression language function table is now at its permanent home: Expression language functions .
Note: The Smartlist and Search - Rules and Modifiers page is now at its permanent home: Smartlist and Search - Rules and Modifiers
Note: The Regex() page is now at its permanent home: MC expression language page
Note: The File Properties page is now at its permanent home: File Properties (tags) page

Caution: Debris Ahead...



Overview

Media Center provides a simple programming language that enhances and enriches its overall user interface and usability. This language, commonly called the expression language, is simple to learn, simple to use, and can greatly enhance your experience using Media Center.

Expressions are ubiquitous throughout Media Center, used in areas such as:

  • The categories in a view
  • File list expression columns
  • Theater View
  • Customized view headers, grouping and sort criteria
  • The library field manager (fields with data type Calculated data)
  • File and folder location definitions
  • Auto-import rules
  • Custom DLNA titles
  • The player's display
  • Captions and thumbnail text
  • The link manager (expressions help format link URLs)
  • Rename, Move, & Copy tool
  • Tag assignment
  • Complex search queries

The Expression Language

An expression is a mixture of ordinary text, pre-defined functions, and a few reserved characters and constructs that have special meaning. An expression is evaluated by Media Center's expression engine and textual output is produced. This output is then used by Media Center to customize the user interface and affect its method of operation.

The Anatomy of an Expression

As mentioned above, an expression is a mixture of text and function calls (and some reserved stuff described shortly). The simplest expression would be some basic, literal text, such as A good movie. The expression engine evaluates this expression, finds nothing special, and then outputs the result: A good movie. Simple.

But simple text only has so much utility. The ability to transform or generate content is much more interesting and useful. And this is when functions are employed. Media Center provides many functions, which when called, produce some output. Most functions require some form of input, called arguments, and most functions generate output. By supplying a function with various arguments, the function will return some output value which is just more text. And this output text can be the used by other functions, and so on. Each function has a unique name, and calling upon a function to do some work requires little more that using its name anywhere in the expression.

A function call looks like this:

functionname(argument 1, argument 2, ...)

The syntax of the function call is the function's case-insensitive name, immediately followed by an opening parenthesis character, one or more comma-separated arguments, and a closing parenthesis character. Whitespace after the commas is optional, but helps readability and formatting. And each argument itself is also just an expression. And some arguments are optional. If an argument is optional, it can be omitted and its default value will be used. If the argument is omitted, a comma-separator will still be required if additional arguments follow. The following example uses the FixCase() function to change its input to Title Case:

fixcase(A good movie)

The result is A Good Movie.

A slightly more complex expression example consists of both text and a nested function call:

Wow! fixcase(replace(A good movie, good, great))

Inner functions are called before outer functions, so the Replace() function is call first:

replace(A good movie, good, great)

and its output is then supplied as the input to the FixCase() function. Replace() does its work substituting good with great, and returns A great movie. This output is then supplied as the argument to FixCase() which sees only the text A great movie (it knows nothing about how it was produced). So the function call:

fixcase(A great movie)

in turn outputs A Great Movie. Now that the functions have produced their output, the final output, including the literal Wow! leading text is

Wow! A Great Movie

Fields

The expression examples thus far have been limited to static literal text. Expressions have much more utility when they use data from other sources, such as a file's metadata. Media Center maintains this metadata in its defined fields. This data is accessed using the Field() function, and its first argument is the case-insensitive name of the field to be accessed. For example, the function call field(album) will return the current* file's value for the album field (* more will be said later about the current file). If the album field contained the value After Hours, the expression:

fixcase(field(album), 3)

would produce AFTER HOURS. First field(album) is evaluated, returning After Hours. The FixCase() function is supplied with this output as its first argument, and its second argument is 3, which happens to specify that it should perform upper-casing.

Because fields are so frequently used in expressions, an abbreviated form exists for accessing their values. This makes it easier to both read and write expressions. Nonetheless, both forms are equivalent. The abbreviated form is simple: immediately surround the field's name with opening and closing square brackets, for example, [album]. The previous example is now written more simply as:

fixcase([album], 3)

Field Values

For the sake of simplicity and clarity, the section above glossed over an important detail regarding how Media Center outputs field values. Recall that Field() is the function used to return the value of a specified field. But Field() also has a second argument that indicates the format of the value that it returns. Because field values are used in a variety of situations, the Field() function can produce output suitably formatted for the requirements. There are two forms of output: one is a nice, friendly human-readable format suitable for use in views or other display locations; the other is a raw format which returns the representation stored internally by Media Center which is useful when uninterpreted values are necessary.

By default, Media Center always outputs the friendly format, so expressions sometimes need to take this into account and chose the format accordingly.

Not used earlier because it is optional, the second argument to the Field() function selects the mode of output: the value 0 selects the raw mode, and the default value of 1 selects the friendly mode. Here are two examples using the date field, the first one outputs the date value in raw format, the second in the friendly format:

field(date, 0)
field(date, 1)

Field Values: Empty, 0, and 1

The Media Center expression language does not strongly differentiate between the numeric value zero 0 and emptiness for numeric field types Integer and Decimal. And in some cases, the numeric value of 1 is treated similarly to the empty value.

When a value of 0 is entered as a numeric field's value, the raw value will be shown as 0, but the display format (as in the file list) will be shown as empty. The empty display allows for less visual noise in the user interface, since a column full of 0 values is not usually helpful. In fact, if you attempt to set a numeric field's value to 0 in the file list, it will immediately be displayed as empty.

Generally this difference is unimportant, except when testing numeric values with IsEmpty() or IsEqual(). It is easy to be fooled when testing such a value if the value shown in a file list is empty. The values shown in the Tag Action Window will reveal the actual raw value, as will an expression column using the field's raw format.

Another consideration for integer fields is that when sorting, a 1 value can sometimes sort indistinguishably from an empty value. The Integer type disc # field is typically empty when an album consists of only one disc, and as such, Media Center will sort the disc # values of empty and 1 identically.

The friendly output of a field can differ, depending on context. For example, in a file list, and empty field will be shown as blank, but in the Rename, Move & Copy tool, it will be output as Unknown Disc # (this ensures no blank values are generated as path components). To test such a field, always use and test against the raw format, and then expressions will be context agnostic.

Expression Language Syntax

Now that the basics have been covered, the more rigorous rules of the expression language syntax can be described.

  • An expression is any sequence of literal text and any number of function calls.
  • Expressions are read and evaluated left to right. Literal text is output unmodified, function calls are evaluated and their return values output.
  • Nested functions calls are evaluated from the innermost function to outermost function, and again, left to right when one function follows another.
  • Field abbreviations are expanded into the equivalent Field() function call
  • A functions is evaluated and its returned value contextually replaces the function call in the expression
  • Within a function's argument list, whitespace is ignored before and after commas, after an opening parenthesis, and before a closing parenthesis.
  • The forward-slash escape character / disables the special meaning of the character that follows it.
  • The escape sequence /# followed by #/ escapes everything inside.
  • To use a literal parenthesis, comma, or whitespace inside of function argument lists, escape them. Whitespace within an argument's value is literal and does not need to be escaped when it is surrounded by other non-whitespace text.
  • An expression may be split into multiple lines, but when it does not satisfy the conditions above regarding whitespace around function parenthesis and commas, use a forward-slash escape as the last character before the newline. Extraneous newlines in the expression editor will produce a trailing ellipsis (...) in the output.

Expression Editors

There are a couple of variations of dialog or edit field used to enter expressions. Some allow multi-line expressions, while others are single line, but can be expanded to multi-line editors. Unfortunately, some single-line editors flatten multi-line expressions into a single line, replacing the newlines with spaces. This author is hopeful this will be rectified someday.

How Expressions Are Evaluated

Expressions are evaluated in the context where they are used. For example, an expression column in a file list is evaluated relative to those files in the file list. And the general flow is that for each file in the list, the expression is evaluated and produces output. The expression only has access to the fields available for the file currently being evaluated. This is important to remember, so it bears repeating. One file after another, an expression is evaluated against that single file, its output is produced and stored away for use later, and then the result of that evaluation is entirely forgotten before the next file is evaluated. This means, the expression evaluator cannot use the results from one file's evaluated expression with the results of another file's evaluation.

Expressions and Locales

Media Center will respect the Windows locale setting for output values produced by certain functions, and within the values of certain fields. This is important to consider when writing expressions that consume such values. Under most circumstances, such values cause no harm. However special care must be taken with functions that require the use of period as the decimal point. One such function is Math(), which always uses period as the decimal point. If your locale uses some other character such as comma, these characters will have to be converted into periods before the critical function is called. Handling this problem is not difficult. Before passing to Math() any floating point number, use Replace() first when necessary to convert the locale's decimal character into a period. Fields that cause problems are any fields that produce floating-point values, such as any Date type field in raw format (e.g. [date,0], [last played,0], [date modified,0], and [date imported,0]), or any textual field that contains floating-point values that will be used for various calculations (e.g. any of the Dynamic Range variants). Certain functions such as Now() and ConvertTime() also return localized floating-point values. Consider also that the expression parser uses comma as the argument separator. Any literal numeric values specified as a function argument must have any embedded commas escaped.

A Complex Expression Example

Expression Editor.png

Here is a more complex expression example that illustrates the various rules discussed above regarding expressions:

if( IsEmpty( [Disc #] ),
Disc number is empty,
Delimit(
field(disc #) ,
/) ,
DISC /(
)
)

The expression demonstrates that

  • whitespace before and after commas or opening and closing parenthesis is ignored
  • expressions can be safely split into multiple lines using the whitespace rules just mentioned
  • function and field names are case insensitive
  • forward slash is used and required to escape parenthesis (see inside the Delimit() function)
  • whitespace does not require escapement when surrounded by other characters (see after the C in DISC)
  • literal text is output unmodified (Disc number is empty)
  • functions can be nested (Both IsEmpty() and Delimit() are nested within the If() function, and the Field() function is nested within Delimit()

When the expression is run, files that have no disc number will produce Disc number is empty, and files that have, say, a disc number value of 3 will produce DISC (3).

Field Assignment

The output of an expression can be used to assign a value to a tag. This is accomplished by preceding the expression with an = character. The = character causes the tagging engine to invoke the expression evaluator first, and then to use its output as the value to assign to the field.

Field Assignment with Expression.png

Without the prepended = character, the literal expression text itself and not its evaluated value would be stored in the tag. The expression can refer to the field's own value to modify itself, and this offers a convenient way to perform complex transformations on field values. For example, the assignment expression

=removeleft([name], 4)

entered into an edit cell for the name field would remove four characters from the left of the name field's current value. An assignment expression can be entered into the Tag Action Window, or by using inline editing in the file list or a pane entry. The image on the right shows in-place field assignment.

Note: Undo is supported, reverting each tag to its value prior to the assignment. Redo is also supported, reapplying the most recent Undo.

Expressions and Search

The expression language is fully available to the search query engine (Search, Set rules for file display, etc.). This allows creation of more complex search queries than would otherwise be possible. An expression-based search query is any valid expression that produces a zero or non-zero numeric output. The syntax of the query is:

[=expression]=numval

where expression is any valid expression, and numval is the expected numeric output produced by the expression. The expression is evaluated against the current list of available files and the expression output numerically compared against numval. All files for which the comparison is true are returned as part of the file list produced by the query and all files that fail the comparison are winnowed from the file list.

The following example illustrates an expression-based search query:

[=ismissing([filename (path)]\Folder.jpg)]=1

The IsMissing() function is run using the file name argument [filename (path)] appended by \Folder.jpg, and returns a Boolean value 1 for files that are missing, and this 1 is compared against the value numval. All these files where there was a successful comparison are returned in the file list, and all those for which the expression produced 0 are filtered from the file list. By inverting the comparison and using a 0 numval, the set of files remaining in the file list would be those that did not match.

Data Types

It was mentioned already that the Media Center expression language is primarily a textual language - it consumes and produces text. Nonetheless, certain areas of Media Center are influenced by the type of data used or presented, and sometimes it is useful or necessary to coerce expression output into one data type or another. Each Media Center field is defined to be of a certain data type, listed in the Field Data Types table. These types influence how values are output, sorted, and interpreted on input. And expressions always output data of type String. By coercing the data type of an expression, output formatting and sorting can be controlled in various ways.

Data types are forced by appending to an expression the string:

&datatype=[type]

where type is one of the following values:

listA list of strings, separated by semicolons
stringSorts as strings (with smart number handling)
numberSorts values as numbers (decimal or integer)
integerSorts values as integers
pathSorts using a smart filename compare style
monthSorts string month names (i.e. January, February, etc.)

Calculated Fields and Search

Media Center's Search supports some simple numeric comparison operators. Because expressions always evaluate as a String type, these operators would be unavailable for use in a search query to compare numeric values from a calculated expression field. In order to use the numeric comparison operators, a calculated expression field can be cast into one of the numeric types. In your numeric calculated fields, to allow the use Search's numeric comparison operators, add either of the casts:

&datatype=[integer]
&datatype=[number]

to the end of the field's calculated expression.

Lists and Trees

Datatype List.png

The list of output in view categories and pane columns can be modified by forcing the data type to a List type. Two things happen when the data type is List: The values within a List type are split into their individual (semicolon-separated) list items The backslash character takes on a special meaning and becomes another form of separator that creates tree-like hierarchies, collapsible in panes columns and creates drill-down categories in any category view type (Standard View > Categories, Theater View, DLNA, Gizmo/WebGizmo). Forcing an expression's type to list causes this list item separation and hierarchy generation. Alternatively, forcing a List type to string defeats this. Add the cast:

&datatype=[list]

to the end of an expression to force an expression's output to be considered as a List type. Conversely, a List type may be forced into a String type by adding the cast:

&datatype=[string]

to the end of an expression.

Sort Order

Datatype Month.png

Normally strings are sorted ASCII-betically with some smart numeric sorting. But this form of sort may not always be desired.

Sorting by Month

Generally it is more useful to see month names sorting such that January sorts before April, instead of alphabetically where April would sort before January. Forcing an expression's type to Month forces string month values to be treated instead as their equivalent numerical month numbers. For example, the first month January and the third month March sort before the fourth month April. Add the cast:

&datatype=[month]

to the end of an expression to force an expression's output to be sorted by numeric month values.

Sorting by Path

Path data types sort using smart filename comparisons.

XXX complete this: Path sorting always seems to be used.

Add the cast:

&datatype=[path]

to the end of an expression to force an expression's output to be smart-sorted by path components.

... and more to come...