Version 2 moods.
[paraslash.git] / web / manual.md
index 96d724c..904ac18 100644 (file)
@@ -338,6 +338,11 @@ libgcrypt are usually shipped with the distro, but you might have
 to install the development package (`libssl-dev` or `libgcrypt-dev`
 on debian systems) as well.
 
+- [flex](https://github.com/westes/flex) and
+[bison](https://www.gnu.org/software/bison) are needed to build the
+mood parser of para_server. The build system will skip para_server
+if these tools are not installed.
+
 - [libmad](http://www.underbit.com/products/mad/). To compile in MP3
 support for paraslash, the development package must be installed. It
 is called `libmad0-dev` on debian-based systems. Note that libmad is
@@ -976,124 +981,140 @@ the score table (but not from the playlist).
 
 <h3> Moods </h3>
 
-A mood consists of a unique name and its *mood definition*, which is
-a set of *mood lines* containing expressions in terms of attributes
-and other data contained in the database.
-
-At any time at most one mood can be *active* which means that
-para_server is going to select only files from that subset of
-admissible files.
-
-So in order to create a mood definition one has to write a set of
-mood lines. Mood lines come in three flavours: Accept lines, deny
-lines and score lines.
-
-The general syntax of the three types of mood lines is
-
-
-       accept [with score <score>] [if] [not] <mood_method> [options]
-       deny [with score <score>] [if] [not] <mood_method> [options]
-       score <score>  [if] [not] <mood_method> [options]
-
-
-Here <score> is either an integer or the string "random" which assigns
-a random score to all matching files. The score value changes the
-order in which admissible files are going to be selected, but is of
-minor importance for this introduction.
-
-So we concentrate on the first two forms, i.e. accept and deny
-lines. As usual, everything in square brackets is optional, i.e.
-accept/deny lines take the following form when ignoring scores:
-
-       accept [if] [not] <mood_method> [options]
-
-and analogously for the deny case. The "if" keyword is only syntactic
-sugar and has no function. The "not" keyword just inverts the result,
-so the essence of a mood line is the mood method part and the options
-following thereafter.
-
-A *mood method* is realized as a function which takes an audio file
-and computes a number from the data contained in the database.
-If this number is non-negative, we say the file *matches* the mood
-method. The file matches the full mood line if it either
-
-       - matches the mood method and the "not" keyword is not given,
-or
-       - does not match the mood method, but the "not" keyword is given.
-
-The set of admissible files for the whole mood is now defined as those
-files which match at least one accept mood line, but no deny mood line.
-More formally, an audio file F is admissible if and only if
-
-       (F ~ AL1 or F ~ AL2...) and not (F ~ DL1 or F ~ DN2 ...)
-
-where AL1, AL2... are the accept lines, DL1, DL2... are the deny
-lines and "~" means "matches".
-
-The cases where no mood lines of accept/deny type are defined need
-special treatment:
-
-       - Neither accept nor deny lines: This treats all files as
-       admissible (in fact, that is the definition of the dummy mood
-       which is activated automatically if no moods are available).
-
-       - Only accept lines: A file is admissible iff it matches at
-       least one accept line:
-
-               F ~ AL1 or F ~ AL2 or ...
-
-       - Only deny lines: A file is admissible iff it matches no
-       deny line:
-
-               not (F ~ DL1 or F ~ DN2 ...)
-
-
-
-<h3> List of mood_methods </h3>
-
-       no_attributes_set
-
-Takes no arguments and matches an audio file if and only if no
-attributes are set.
-
-       is_set <attribute_name>
-
-Takes the name of an attribute and matches iff that attribute is set.
-
-       path_matches <pattern>
-
-Takes a filename pattern and matches iff the path of the audio file
-matches the pattern.
-
-       artist_matches <pattern>
-       album_matches <pattern>
-       title_matches <pattern>
-       comment_matches <pattern>
-
-Takes an extended regular expression and matches iff the text of the
-corresponding tag of the audio file matches the pattern. If the tag
-is not set, the empty string is matched against the pattern.
-
-       year ~ <num>
-       bitrate ~ <num>
-       frequency ~ <num>
-       channels ~ <num>
-       num_played ~ <num>
-       image_id ~ <num>
-       lyrics_id ~ <num>
-
-Takes a comparator ~ of the set {<, =, <=, >, >=, !=} and a number
-<num>. Matches an audio file iff the condition <val> ~ <num> is
-satisfied where val is the corresponding value of the audio file
-(value of the year tag, bitrate in kbit/s, etc.).
-
-The year tag is special as its value is undefined if the audio file
-has no year tag or the content of the year tag is not a number. Such
-audio files never match. Another difference is the special treatment
-if the year tag is a two-digit number. In this case either 1900 or
-2000 is added to the tag value, depending on whether the number is
-greater than 2000 plus the current year.
-
+A mood consists of a unique name and a definition. The definition
+is an expression which describes which audio files are considered
+admissible. At any time at most one mood can be active, meaning
+that para_server will only stream files which are admissible for the
+active mood.
+
+The expression may refer to attributes and other metadata stored in
+the database. Expressions may be combined by means of logical and
+arithmetical operators in a natural way. Moreover, string matching
+based on regular expression or wildcard patterns is supported.
+
+The set of admissible files is determined by applying the expression
+to each audio file in turn. For a mood definition to be valid, its
+expression must evaluate to a number, a string or a boolean value
+("true" or "false"). For numbers, any value other than zero means the
+file is admissible. For strings, any non-empty string indicates an
+admissible file. For boolean values, true means admissible and false
+means not admissible.  As a special case, the empty expression treats
+all files as admissible.
+
+<h3> Mood grammar </h3>
+
+Expressions are based on a context-free grammar which distinguishes
+between several types for syntactic units or groupings. The grammar
+defines a set of keywords which have a type and a corresponding
+semantic value, as shown in the following table.
+
+Keyword              |    Type | Semantic value
+:--------------------|--------:|:----------------------------------
+`path`               |  string | Full path of the current audio file
+`artist`             |  string | Content of the artist meta tag
+`title`              |  string | Content of the title meta tag
+`album`              |  string | Content of the album meta tag
+`comment`            |  string | Content of the somment meta tag
+`num_attributes_set` | integer | Number of attributes which are set
+`year`               | integer | Content of the year meta tag [\*]
+`num_played`         | integer | How many times the file has been streamed
+`image_id`           | integer | The identifier of the (cover art) image
+`lyrics_id`          | integer | The identifier of the lyrics blob
+`bitrate`            | integer | The average bitrate
+`frequency`          | integer | The output sample rate
+`channels`           | integer | The number of channels
+`is_set("foo")`      | boolean | True if attribute "foo" is set.
+
+[\*] For most audio formats, the year tag is stored as a string. It
+is converted to an integer by the mood parser. If the audio file
+has no year tag or the content of the year tag is not a number, the
+semantic value is zero. A special convention applies if the year tag
+is a one-digit or a two-digit number. In this case 1900 is added to
+the tag value.
+
+Expressions may be grouped using parentheses, logical and
+arithmetical operators or string matching operators. The following
+table lists the available operators.
+
+Token  | Meaning
+:------|:-------
+`\|\|` | Logical Or
+`&&`   | Logical And
+`!`    | Logical Not
+`==`   | Equal (can be applied to all types)
+`!=`   | Not equal. Likewise
+`<`    | Less than
+`<=`   | Less or equal
+`>=`   | Greater or equal
+`+`    | Arithmetical minus
+`-`    | Binary/unary minus
+`*`    | Multiplication
+`/`    | Division
+`=~`   | Regular expression match
+`=\|`  | Filename match
+
+Besides integers, strings and booleans there is an additional type
+which describes regular expression or wildcard patterns. Patterns
+are not just strings because they also include a list of flags which
+modify matching behaviour.
+
+Regular expression patterns are of the form `/pattern/[flags]`. That
+is, the pattern is delimited by slashes, and is followed by zero or
+more characters, each specifying a flag according to the following
+table
+
+Flag |    POSIX name | Meaning
+:----|--------------:|--------
+`i`  |   `REG_ICASE` | Ignore case in match
+`n`  | `REG_NEWLINE` | Treat newline as an ordinary character
+
+Note that only extended regular expression patterns are supported. See
+regex(3) for details.
+
+Wildcard patterns are similar, but the pattern must be delimited by
+`'|'` characters rather than slashes. For wildcard patterns different
+flags exist, as shown below.
+
+Flag |             POSIX name | Meaning
+:----|-----------------------:|--------
+`n`  | `FNM_NOESCAPE`         | Treat backslash as an ordinary character
+`p`  | `FNM_PATHNAME`         | Match a slash only with a slash in pattern
+`P`  | `FNM_PERIOD`           | Leading period has to be matched exactly
+`l`  | `FNM_LEADING_DIR` [\*] | Ignore "/\*" rest after successful matching
+`i`  | `FNM_CASEFOLD` [\*]    | Ignore case in match
+`e`  | `FNM_EXTMATCH` [\*\*]  | Enable extended pattern matching
+
+[\*] Not in POSIX, but both FreeBSD and NetBSD have it.
+
+[\*\*] GNU extension, silently ignored on non GNU systems.
+
+See fnmatch(3) for details.
+
+Mood definitions may contain arbitrary whitespace and comments.
+A comment is a word beginning with #. This word and all remaining
+characters of the line are ignored.
+
+<h3> Example moods </h3>
+
+* Files with no/invalid year tag: `year == 0`
+
+* Only oldies: `year != 0 && year < 1980`
+
+* Only 80's Rock or Metal: `(year >= 1980 && year < 1990) &&
+  (is_set("rock") || is_set("metal"))`
+
+* Files with incomplete tags: `artist == "" || title == "" || album =
+"" || comment == "" || year == 0`
+
+* Files with no attributes defined so far: `num_attributes_set == 0`
+
+* Only newly added files: `num_played == 0`
+
+* Only poor quality files: `bitrate < 96`
+
+* Cope with different spellings of Motörhead: `artist =~ /mot(ö|oe{0,1})rhead/i`
+
+* The same with extended wildcard patterns: `artist =| |mot+(o\|oe\|ö)rhead|ie`
 
 <h3> Mood usage </h3>
 
@@ -1122,27 +1143,6 @@ if the "-a" switch is given:
 
        para ls -a
 
-
-<h3> Example mood definition </h3>
-
-Suppose you have defined attributes "punk" and "rock" and want to define
-a mood containing only Punk-Rock songs. That is, an audio file should be
-admissible if and only if both attributes are set. Since
-
-       punk and rock
-
-is obviously the same as
-
-       not (not punk or not rock)
-
-(de Morgan's rule), a mood definition that selects only Punk-Rock
-songs is
-
-       deny if not is_set punk
-       deny if not is_set rock
-
-
-
 File renames and content changes
 --------------------------------