- 1 The Anatomy of an Expression
- 2 Fields
- 3 Expression Language Syntax
- 4 Functions
- 4.1 Function Arguments
- 4.2 Function Index
- 4.2.1 Accessing and Storing Functions
- 4.2.2 Conditional Functions
- 4.2.3 Date and Time Functions
- 4.2.4 File Path and Identifier Functions
- 4.2.5 Formatting Functions
- 4.2.6 Grouping Functions
- 4.2.7 List Manipulation Functions
- 4.2.8 Miscellaneous Functions
- 4.2.9 Number Functions
- 4.2.10 String Manipulation Functions
- 4.2.11 Test and Comparison Functions
- 5 Data Types
- 6 Expressions and Search
- 7 HTML Font Properties
- 8 Expression Editors
- 9 Acknowledgements
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
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
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:
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 called square bracket notation 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:
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 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 (0) 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.
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.
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
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.
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.
- Fields designated using square bracket notation are expanded into the equivalent Field() function call.
- Nested function calls are evaluated from the innermost function to outermost function, and again, left to right when one function follows another.
- A function 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.
- The escape sequence /* followed by /* will escape everything inside returning it as given without formatting or processing.
- 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.
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
Here is a more complex expression example that illustrates the various rules discussed above regarding expressions:
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).
Functions enable you to transform or generate content automatically. For background information on how functions are used in expressions, refer to The Anatomy of an Expression section above.
This section describes all the various functions provided by Media Center's Expression Language.
As discussed above, a function call consists of the function's case-insensitive name, immediately followed by an opening parenthesis character, one or more comma-separated arguments, and a closing parenthesis character:
Functions may have one or more arguments. In some cases, these arguments are optional, and will automatically use a default value if omitted. For example, these two expressions are equivalent because the mode argument for IsEmpty() is optional and defaults to 0:
- isempty([comment], 0)
In this case, a comma-separator will still be required if additional arguments follow the optional one. Whitespace after the commas is also optional, but helps readability and formatting. However, if any extra arguments are included in a function call which are not valid for that function, they are dropped and omitted from output. This is of particular importance when using string-manipulation functions on text. If the text you enter contains any commas, these must be escaped or the expression engine will consider text after the comma to be additional arguments.
The arguments themselves are also just expressions, and you can nest multiple functions to achieve complex logic:
- if(isequal([artist], [album], 1), Eponymous, [album])
Nested function calls are always treated as a single argument when used as the input to another function (so that commas in the output of one function do not need to be escaped to be used as input in another function). This includes fields, since they are expanded to the equivalent Field() function call.
- Please Note: In some cases below, such as with Unswap(), we have ignored this detail in order to simplify the examples. If you enter any text manually into a function, all commas must be escaped in order to achieve the correct result. This detail is unimportant in most real-world usages, however, because you will typically use either field values or the output of other functions as the arguments in your expressions. However, if you need to include commas in a string literal argument to a function, you need to escape every one, or block escape the entire argument.
Unfortunately, when these were actually introduced has not been tracked. The only way to be certain you have access to all of the functions below is to make sure you have the most current build of Media Center installed.
The available functions are grouped below based on the type of operation they might perform. If you prefer, a flat, alphabetically sorted function list is available here.
Over time, as Media Center evolves, expression functions are added or changed. Those changes are typically not reflected here immediately. In the list available here, on interact, any entries in red text are currently missing from these wiki pages.
- Field(…): Returns a field's value.
- FieldQuery(…): Return a list of matches based on a list of fields to search, from a selected scope of files.
- ItemCount(…): Counts the number of files that have the exact same value of the given expression as the file the expression runs in the context of.
- Load(…): Outputs the value of a global variable.
- Note(…): Retrieve note fields.
- Save(…): Saves a value to a global variable.
- SaveAdd(…): Adds to a global variable.
- SetField(…): Sets a field's value.
- Tag(…): Returns a file's physical tag.
- And(…): Tests a set of values and returns 1 if all are true.
- FirstNotEmpty(…): Returns the first non-empty argument.
- If(…): Conditional ifelse evaluator.
- IfCase(…): Functions as a switch or select case statement.
- IfElse(…): Conditional if-elseif evaluator.
- Not(…): Negates the results of funtions.
- Or(…): Tests a set of values and returns 1 if any are true.
- ConvertDate(…): Converts a human-readable date to the internal format required for use in date fields
- DateInRange(…): Compares a date with a range of dates
- FormatDate(…): Formats a date value in a specified manner
- Now(…): Retrieve and display the system date
- PlaylistTime(…): Returns the time of a track in the current playlist (a sum of all previous durations)
- DBLocation(…): Identifies a file's databases
- Enviro(…): Returns the full path to a host system variable
- FileDBLocation(…): Identifies a file's databases
- FileFolder(…): Returns the name of a file's parent
- FileKey(…): Returns a file's unique internal identifier
- FileLookup(): Looks up a file based on its filename
- FileName(…): Returns a file's name component
- FilePath(…): Returns a file's path component
- FileVolume(…): Returns a file's volume name component
- Delimit(…): Outputs a value with head/tail strings when value is non-empty
- FormatBoolean(…): Formats a boolean (true / false) value in a specified manner
- FormatDuration(…): Presents a duration of seconds in a reader friendly format
- FormatFileSize(…): Presents a number of bytes in a reader friendly format
- FormatNumber(…): Formats and rounds a number to a specified number of decimal places
- FormatRange(…): Formats a value as a range
- Orientation(…): Outputs the orientation of an image
- PadNumber(…): Adds leading zeros to any given number
- RatingStars(…): Outputs the value of Rating as a number of star characters
- RatingStars10(…): Outputs the value of a 10 star rating field as a number of star characters
- Watched(…): Outputs a formatted video bookmark
- GroupCount(…): Counts the members of a specified group (in a category or field).
- GroupCountQuery(…): Globally counts the number of items in a specified group.
- GroupSummary(…): Smartly summarizes the members of a specified group (mode, mean, min, max, etc as is most logical for that grouping).
- GroupSummaryQuery(…): Get a summary for the current group of files based on another matching field.
- ListBuild(…): Constructs a list from a series of items
- ListClean(…): Various list operations
- ListCombine(…): Combines two delimited lists into a single delimited list
- ListContains(…): Checks for a value being in a list
- ListCount(…): Returns the number of items in a list
- ListEqual(…): Checks for equality between two lists
- ListFilter(…): Filter any list, returning only values within a given range
- ListFind(…): Search a list for a value and return that value, or its index # in the list
- ListFormat(…): Outputs a given list in a reader friendly format.
- ListGrep(…): Returns list items containing specified text
- ListItem(…): Returns an item from a location in a list
- ListLimit(…): Limits the length of a list
- ListMath(…): Perform one of 4 specific math functions on a list containing numbers
- ListMix(…): Combine corresponding values from multiple lists into a new list, using a template to process each item
- ListRemove(…): Removes a string from a list
- ListShuffle(…): Shuffles a list
- ListSort(…): Sort a list of values
- AlbumArtist(…): Returns a file's calculated album artist
- AlbumKey(…): Returns a unique album key for a file
- AlbumType(…): Returns the album type for a file
- AudioAnalysisState(…): Returns the state of audio analysis for a file
- Char(…): Returns a character from the numeric code of that character
- CustomData(…): Returns internal data to the expression language
- FilePlaylists(…): Returns a list of playlists a file belongs to (Can also be used to search)
- Literal(…): Returns a string as given without any formatting or processing
- Repeat(…): Returns any given string repeated the specified number of times
- Row(…): Returns the row number of a list entry
- Size(…): Returns a file's size in a format specific to the media type
- Translate(…): Converts an English string found in the program to the current language selected in the language menu
- TreeNode(…): Returns the selected tree path
- TVInfo(…): Miscellaneous television and other pre-formatted information
- Avg(…): Returns the average from a set of numbers
- Counter(…): Counts upwards in specified increments
- Math(…): Evaluates a given mathematical formula
- Max(…): Returns the largest value from a set of numbers
- Min(…): Returns the smallest value from a set of numbers
- Number(…): Returns the first number , including decimals, from a given string
- Rand(…): Returns a random number anywhere between two given numbers
- Range(…): Creates a semi-colon delimited list of numbers in a field
- Roman(…): Converts any given number to, or from, roman numerals
- StackCount(…): Returns the number of files in a stack
- Sum(…): Returns the sum of a set of numbers
- TrackNumber(…): Returns a file's track # value
- Clean(…): Clean a string to be used for various operations
- Extract(…): Returns a portion of a string bounded by another substring
- Find(…): Finds a string or character in another string, returning its zero-based position in that string
- FixCase(…): Changes the case of a given string
- FixSpacing(…): Intelligently splits adjacent camel-cased words
- Hexify(…): Hexifies a string to make it suitable for web usage
- Left(…): Retrieves a specified number of characters from the left of a string
- Length(…): Returns the number of characters in a string
- Letter(…): Returns the starting letter or letters of a given string
- Mid(…): Retrieves specified characters from a string
- MoveArticles(…): Takes "The Beatles" and reverses it to "Beatles, The"
- NoArticles(…): Takes "The Beatles" and returns "Beatles"
- PadLeft(…): Pad any string with any character, to the left
- PadRight(…): Pad any string with any character, to the right
- Regex(…): Regular expression pattern matching and capture
- RemoveCharacters(…): Removes a list of characters from a string
- RemoveLeft(…): Trims characters from the beginning of a string
- RemoveRight(…): Trims characters from the end of a string
- Replace(…): Replace or remove a string segment
- Right(…): Retrieves a specified number of characters from the right of a string
- Swap(…): Takes Firstname Lastname and swaps to Lastname, Firstname
- Trim(…): Removes leading and trailing non-printable characters and new lines from a string
- TrimLines(…): Removes leading and trailing non-printable characters and new lines from a string
- UnMoveArticles(…): Takes "Beatles, The" and reverses it to restore the normal word order, "The Beatles"
- Unswap(…): Takes Lastname, Firstname and reverses it to Firstname Lastname
- Urlify(…): Takes a string and applies html formatting for browser consumption
- Compare(…): Compares two numbers
- IsDigit(…): Determines whether or not a given value is digits
- IsDriveMissing(…): Checks if a drive is missing
- IsEmpty(…): Tests a value for emptiness
- IsEqual(…): Compares two values in one of seventeen specified modes
- IsInPlayingNow(…): Tests to see if a file is in the Playing Now playlist
- IsLowerCase(…): Tests to see if a value is lower case
- IsMissing(…): Tests to see if a file exists on the system
- IsPlaying(…): Tests to see if a file is in currently being played
- IsRange(…): Tests a value for inclusion within a given range
- IsRemovable(…): Tests to see if a file is stored on removable media
- IsUpperCase(…): Tests to see if a value is upper case
- SearchTags(…): Finds all fields that contain a value
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:
where type is one of the following values:
|list||A list of strings, separated by semicolons|
|string||Sorts as strings (with smart number handling)|
|number||Sorts values as numbers (decimal or integer)|
|integer||Sorts values as integers|
|path||Sorts using a smart filename compare style|
|month||Sorts string month names (i.e. January, February, etc.)|
Calculated Fields and Search
Media Center's Search Language 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:
to the end of the field's calculated expression.
Lists and Trees
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:
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:
to the end of an expression.
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:
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: Note: This section is incomplete. I cannot distingish any difference between using a datatype of path vs. string. It seems path sort order is always engaged.
Add the cast:
to the end of an expression to force an expression's output to be smart-sorted by path components.
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:
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:
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.
HTML Font Properties
The expression language recognizes a limited set of HTML font properties and attributes. These can be used to set font styles in most text drawing areas, such as captions, thumbnail text and in the configuration of Theater View. HTML tags are used by surrounding the desired content with an opening and closing tag, in the form of:
- <tag>desired content<//tag>
The supported HTML tags are:
|<font>||Font properties (see attributes below)|
The font tag supports the following attributes:
|alpha||Sets alpha-blending percentage (0 - 100)|
|color||Sets the foreground color (RGB hex values from 00 to ff in the form of rrggbb)|
|bgcolor||Sets the background color (same values as color)|
|face||Sets the font face (a font name)|
|size||Sets the font size (a percentage scaling value)|
Any combination of HTML tags and font attributes can be used. An HTML tag must have an opening and closing tag. Nesting is allowed, but be sure to properly balance like opening and closing tags. Attribute values must be double quoted. The closing tag's forward slash requires escapement with an extra forward slash. The following examples illustrate using HTML font properties:
- <i>This is in italics<//i>
- <i><b>And this is bold and italic<//b><//i>
- <b>The<font color="ff0000" size="80" alpha="50"> Great <//font>Gatsby<//b>
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.
A big tip of hat to marko who tackled the enormous challenge of documenting the MC Expression Language in detail. His work was instrumental and through which has brought clarity and great assistance to Media Center users worldwide.
Also, a huge thanks to user MrC who built the amazing and long-lived previous version of this page, upon which this is still heavily based.
The current caretaker of this documentation is forever in their debts.