pianod2 Documentation
pianod2
is a music player as a UNIX daemon,
supporting Pandora and local music collections. The football socket
library exposes both IPv4 and IPv6 sockets for control, which can
be done via nc
(1), telnet
(1), or via a
Websocket connection from a browser. The included web client can
connect via HTTP or HTTPS (if enabled/configured).
The package includes piano
, a shell script to
access and control the music server. This allows control from other
shell scripts, and provides a reference implementation for
communicating with the daemon over the line protocol.
Warning: pianod2
line protocol is
incompatible with that of the original pianod
. And you
might want to use the JSON protocol anyway.
This documentation is include with pianod2
in
Markdown format. It is also available in HTML.
pianod2
Protocols
pianod2
supports 2 protocols:
- A text-based protocol in the traditions of FTP, SMTP, POP, and other UNIX services. Use port 4445.
- A more modern-style, JSON protocol. Connect to port 4446 and
issue the greeting
"HELO pianod json\n"
. - Either of these can be used over TLS by using port 4447 and issuing the greeting after the TLS handshake.
- To use JSON over a websocket connection, include the query
parameter
protocol=json
when opening the websocket connection to port 4446 or 4447.
These procedures select the response protocol. Commands/requests can be issued in either form at any time.
For clarity, “commands” refers to command-line style directives, while “request” refers to JSON style.
Common elements of the protocols are:
- TCP/IP Line Framing
- For both text and JSON protocols over plain-old TCP, commands/requests are submitted as a line of text terminated by a newline. Newlines are also key in framing responses.
- WebSocket Framing
- When using WebSockets, each line is instead a WebSocket packet, for both commands/requests and responses. Newlines are omitted from WebSocket responses.
- Mingling of spontaneous information and replies
- pianod transmits both spontaneous information (such as playback status changes and current track information) and replies to clients. Clients must be able to separate the spontaneous from the replies. The protocols have been designed to allow this.
- Reply & Ordering Guarantee
- Furthermore, a command or request is guaranteed to receive a reply, and replies will arrive in the order in which requests were sent. Thus, a client may submit successive requests without waiting for replies, and replies can be handled (typically dispatched to callback functions) as they are received.
- No Guarantee of Responsiveness
-
There is no guarantee how quickly the response will be provided. Clients should avoid using timeouts, or set them sufficiently high that normal delayed responses will not trigger them. Delays occur:
- around song transitions & crossfades
- preparing responses to complicated queries
- if other connected users/sessions are submitting requests
- if Internet music services are slow in responding due to network or server congestion
- if pianod is on the Internet and being probed heavily for exploits
pianod2 JSON Support
JSON Requests
JSON request names and keys are case sensitive.
Message quoting should be done in accordance with the JSON specification.
When sent to pianod
, the request must be sent on a
single line; returns are treated as end-of-message. Requests split
over multiple lines will fail with parse errors. Only one request
can be sent per line.
Any instruction can be sent as either a command line or
as a JSON request. The SCHEMA
command (or its
equivalent request, getSchema
) generates a list of all
requests, or details for a particular request. See the commands
section of this documentation for a more information on
commands.
For example:
SCHEMA getSchema
203 Data
132 Information: All requests take the form:
132 Information: {"requestname": {request parameters/options...}}
132 Information: getSchema: Dictionary containing:
132 Information: 'request' (optional)
132 Information: List (0 or more members) of String (no control characters allowed)
204 No data or end of data
If we wanted to use JSON to request the schema for authenticating:
{ "getSchema": {"request": ["authenticate"] } }
203 Data
132 Information: authenticate: Dictionary containing:
132 Information: 'username' (mandatory)
132 Information: Requires also: password
132 Information: String (no control characters allowed)
132 Information: 'password' (mandatory)
132 Information: Requires also: username
132 Information: String (no control characters allowed)
204 No data or end of data
There are 3 special keys that can be used in the main dictionary of the request:
asUser
- Accepts a dictionary containing
username
andpassword
(both strings). Like theAS USER
command-line counterpart, requests includingasUser
will close the connection when they complete (or fail). inRoom
- accepts a dictionary containing
room
(string). withSource
- Accepts a dictionary named
source
containing eitherid
(numeric) or bothtype
andname
(both strings).
JSON Responses
JSON response mode is selected at connection time. Initiate
Websocket connections with the query parameter
protocol=json
, or connect on
pianod
’s HTTP or HTTPS port and greet with
HELO pianod json\n
for JSON over plain TCP.
All messages sent by the server are dictionaries, which take 3 forms:
- Replies (i.e., Successes/failures)
- Data
- Notifications
Messages are sent as a single, newline-terminated line, or a single Websocket message.
In response to a command or request, you will receive
either a reply or data. These both have the fields
code
, which matches up with the success/command error
codes for the line protocol.
Data
If code
is 203, the message contains the reply data
in a member called data
. data
is
always a list of dictionaries:
USER LIST PERETTE
{
"code":203,
"data":[
{
"id":"perette",
"privileges":{
"rank":"admin",
"deejay":false,
"present":true,
"service":false,
"influence":true,
"tuner":false,
"shadow":false
}
}
],
"status":"Data"
}
Note: Response message has been formatted for
legibility. The server will return it as a single line of
JSON, without newlines or identing. A command-line utility
json_format
, included in src/parsnip, will beautify
JSON provided to it. Use it as a filter, or specify filenames on
the command line.
Replies
Other 2xx values for code
(200–202,
205–299) indicate a success, and 400–499 indicate a
command error. For these, there will be both
successes
and failures
arrays. One or
both of these will be non-empty; it is possible to receive multiple
successes, failures, or a mix of both.
GRANT SERVICE TO Frank Edward
{
"code":404,
"successes":[],
"status":"Requested item not found",
"failures":[
{
"code":404,
"status":"Requested item not found",
"details":null,
"id":"Frank",
"name":"Frank"
}, {
"code":404,
"status":"Requested item not found",
"details":null,
"id":"Edward",
"name":"Edward"
}
]
}
Notifications
If there is no code
present, the message is a
notification. Notifications can also be included with
replies.
There are 4 categories of notifications: events
,
state
, currentSong
, and
errors
. Here is a state notification:
{
"state":{
"volume":3
}
}
And another, this one featuring current playback information:
{
"state":{
"playbackState":"Paused"
},
"currentSong":{
"duration":309,
"timeIndex":4,
"timeRemaining":304
}
}
An event notification:
{
"events":[
{
"code":4,
"status":"Track playback complete",
"details":null
}
]
}
A current song notification:
{
"currentSong":{
"playlistName":"jazz",
"songInfoUrl":null,
"duration":456,
"name":"Ritual",
"playlistId":"2pp1080572466",
"source":{
"id":2,
"type":"filesystem",
"name":"perette"
},
"id":"2ss971186724-9",
"artistId":"2sa1792080946",
"artistName":"Chick Corea Elektric Band II",
"compilation":false,
"genre":"Jazz",
"trackId":"2ss971186724-9",
"albumArtUrl":null,
"albumId":"2sl971186724",
"trackName":"Ritual",
"year":1993
}
}
Notifications can carry several categories. In the following
message, currentSong
is null
to indicate
there is no current song because it was stopping.
{
"events":[
{
"code":4,
"status":"Track playback complete",
"details":null
}
],
"state":{
"playbackState":"idle"
},
"currentSong":null
}
Were instead play was continuing to a new track, the track playback complete event would be accompanied by the new track information:
{
"events":[
{
"code":4,
"status":"Track playback complete",
"details":null
}
],
"currentSong":{
"playlistName":"Short Stop",
"songInfoUrl":null,
"duration":313,
"name":"One Of Our Submarines",
"playlistId":"2pp1407280692",
"source":{
"id":2,
"type":"filesystem",
"name":"perette"
},
"id":"2ss1104797890-9",
"artistId":"2sa988499006",
"artistName":"Thomas Dolby",
"compilation":false,
"genre":"Rock",
"trackId":"2ss1104797890-9",
"albumArtUrl":null,
"albumId":"2sl1104797890",
"trackName":"One Of Our Submarines",
"year":null
}
}
pianod2
Text
Protocol
Following in the traditions of FTP, SMTP, POP, and other UNIX
services, pianod2
accepts single-line commands and
returns responses in the form of:
nnn Descriptive Text Here
A command is a series of terms separated by spaces. Terms are either:
- A bare word
- A series of words surrounded by double quotes. The opening double quote must immediately precede the first word, the closing quote must immediately follow the last word. Quotes in other places are treated as part of the term. There is no literal mechanism.
For example:
rename "Classical, Choral" to "Classical, Artistic Moaning"
This holds for all commands except filter expressions. A filter expression is parsed independently and obeys different rules. A filter expression must not be quoted:
SONG LIST WHERE ARTIST=“Madonna” & ALBUM=~”Virgin”
The filter expression starts at “ARTIST”. See Filter Grammar for details.
Response format
pianod2
responses take two forms:
nnn Descriptive Text
nnn Title: Value
nnn
is a numerical status that should be used when
interfacing with with other software.
- For status message, descriptive text provides an explanation and possibly details. It is not internationalized but is subject to change.
- For data messages, titles are composed of exactly one word (or use CamelCase), which is followed by colon, space, and a value.
Unlike many network services, pianod2
may
spontaneously generate messages in response to playback changes,
track changes, music mix changes, etc. The 6 categories of messages
are arranged to make separating the command-response messages from
spontaneous messages.
See the code (response.h) for assigned field numbers. Below are details for special cases.
Status Messages
- 000–099
- Status. These indicate status that is not command-related.
- 100–199
- Data. These identify specific pieces of information sent by the server. These messages may occur spontaneously or as part of a data response to a command; see [Data Responses][].
- 200–299
- Success messages. These occur in response to commands.
- 300–399
- Error detail messages. Zero or more of these precede a 400-class message and provide additional details relating to that error.
- 400–499
- Command error messages. These occur in response to a command.
- 500–599
- Other server errors. These indicate problems that are not command-related. For example, a server being down.
In most cases, exactly one of either a 200- or
a 400-group message will occur in response to a command; the
exception to this is 203 Data
responses. One or more
300-group messages may occur as a result of command, and always
supplement or qualify a subsequent 400-group message.
Playback Status (001–009)
Messages 001–019 indicate playback status.
- 001 (
playbackState: playing
), 002 (paused
) & 003 (stalled
) -
Playback is playing, paused, and stalled respectively. The formatting for the messages is:
001 Playing: now/length/remain 002 Paused: now/length/remain 003 Stalled: now/length/remain
For example:
001 Playing: 02:27/04:12/-01:45
Stalled indicates the player should be playing, but is not, typically because of a buffer underrun caused by network issues.
- 004 (event code: 4)
- The playing track has ended. This represents an event as opposed to a state.
- 005 (
playbackState: betweenTracks
) - The player is between tracks but cuing a song.
- 006 (
playbackState: idle
) - There is nothing playing or paused and the player will not start anything new for any reason: the player is paused, queue mode is stopped, there are no playlists selected, the queue mode is requests-only but queue empty, etc.
Queue mode
Regarding the difference between player state and queue mode: the player state is the CD player, the queue mode is the DJ.
- 007 (
queueMode: stopped
) - Stopped.
- 008 (
queueMode: requests
) - Request-only mode.
- 009 (
queueMode: random
) - Random play mode. If there are no requests, random selections will be played.
Selections
- 011 (
selectedSource
) -
Selected source. This message indicates the source that will be used when processing requests on this connection. The response format is:
011 SelectedSource: id type name
For example:
011 SelectedSource: 1 manager Pianod
011 SelectedSource: 2 tonegenerator Pianod
- 012 (
selectedPlaylist
) -
Selected playlist for this room, which may not coincide with the playing playlist: first, the playlist may have been changed since the song started playing; and second, the selected playlist may be a mix or everything playlist, which encapsulates other playlists. The data format is:
012 SelectedPlaylist: type name
where type is one of
mix
(a manually-selected playlist mix),auto
(an autotuned playlist mix),everything
(all playlists are mixing), orplaylist
(a single playlist). For example:012 SelectedPlaylist: mix Foobar's QuickMix 012 SelectedPlaylist: playlist Jazz Fusion 012 SelectedPlaylist: everything Metamix Bibliotheque
Event Notifications
Event notifications simply alert that something happened or changed, but provide no additional detail. It is up the client to request data if appropriate.
- 021
- Mix selections have changed.
- 022
- The list of playlists has changed. Implies possible mix change. If a parameter is included, it is the ID of the playlist effected.
- 023
- Playlist ratings have changed. Broadcast per-user.
- 024
- Sources available have changed. Implies possible playlist change.
- 025
- Song rating changed. Parameter is ID of song.
- 026
- Queue changed. Indicates insertion, removal or reorder of the queue. Not sent when playback starts; queue advance in this case is implied by playback status message.
Unusual Data Fields
- 116 (
songRating
) -
Track rating information. The rating line always includes the rating as an adjective, then a numeric value (0.5–5.0; 0 indicates unrated), then 0 or more seed indications. Some examples:
116 Rating: good 4.0 seed 116 Rating: unheard 0.0 artistseed albumseed 116 Rating: repugnant 0.5
Rating adjectives includes: Unrated, repugnant, awful, bad, poor, lackluster, neutral, okay, good, excellent and superb.
Seed indications include: seed, albumseed, artistseed. Instead of presence/nonpresence, JSON protocol uses a dictionary:
"songRating":{ "rating":3.0, // will be "null" for unrated. "ratingText":"neutral", // "unrated" for unrated. "artistSeed":false, "albumSeed":false, "seed":false }
- 120 (
playlistRating
) - Playlist rating, which is per
pianod2
user. Spontaneous120
s apply to the playing track’s playlist, not the selected playlist. Format and adjectives are similar to 116. Like 116, JSON uses a dictionary. - 127 (
actions
) -
Actions. Indicates actions that may be taken for a given item. In addition to source capability variances, queue items may differ from search results (song suggestions) and seed listings (song seeds). Values include:
request
(the item may be requested),rate
(the item may be rated),seed
,albumseed
,artistseed
. Seeds apply to playlist actions; when included for a song, they indicate that the song may be seeded on the playlist from which it originates.JSON instead uses a dictionary.
Data responses (203, 204)
Data responses occur in response to requests for playlist lists, current song, song queue, song history, etc. Data fields use the same numbering in both the response and spontaneous contexts, however, it is guaranteed that spontaneous data messages (100–199) will not occur between the initial 203 and final 204 of a response, allowing responses to be separated from other messages.
No Data
A single 204 End of data
response occurs.
HISTORY LIST
204 No data or end of data
Single field list
If data is available, then there is a single 203
Data
, followed by 1 or more data items (100–199), and
finally a 204 End of data
.
SOURCE TYPES LIST
203 Data
123 Source: manager
123 Source: pandora
123 Source: tonegenerator
204 No data or end of data
PLAYLISTS LIST NAMES
203 Data request ok
115 Playlist: Hard Rock Strength Training Radio
115 Playlist: New Age Beats Radio
115 Playlist: Chillout Radio
115 Playlist: Disco
115 Playlist: Symphonic, Classical Period
115 Playlist: Jazz Fusion
204 No data or end of data
Multi field list
If data is available, then there are one or more 203
Data
responses, each followed by the individual data items.
Following the last data group, a 204 End of Data
occurs.
QUEUE LIST
203 Data request ok
111 ArtistID: S479098
112 Album: Big 6
113 Artist: Blue Mitchell
114 Title: Sir John
115 Playlist: Jazz Fusion
203 Data request ok
111 ArtistID: S278345
112 Album: Look At All The Love We Found: A Tribute To Sublime
113 Artist: The Greyboy Allstars
114 Title: Doin' Time
115 Playlist: Jazz Fusion
203 Data request ok
111 ArtistID: S551643
112 Album: Tutu
113 Artist: Miles Davis
114 Title: Portia
115 Playlist: Jazz Fusion
204 No data or end of data
Note that fields in records may not be homogenous; queue items from multiple sources, for example, may have differing fields. Playlist seeds is another example, where depending each the seed’s type (artist vs. album vs. song) included fields will vary.
pianod2 Commands & Text Protocol
Warning: Although similar to the original
pianod
command set, enhancements in
pianod2
are incompatible. If you are working with the
original pianod, refer
to the documentation included with the original.
Commands are not case sensitive. Quoting may be done with single or double quotes, but must match. Starting and ending quotes must respectively lead a word and trail words. The quote may be inserted into the string by doubling it in place:
- “don’t stop”: don’t stop
- ‘don''t stop’: don’t stop
- “ain’t got nothin'”: ain’t got nothin'
- “ain''t got nothin''”: ain''t got nothin''
- ‘ain''t got nothin''’: ain’t got nothin'
- ‘ain''t got nothin''’ “don’t stop”: ain’t got nothin', don’t stop (2 terms)
- ‘ain’t got nothin'' “don’t stop”: ain’t got nothin’ “don’t stop” (1 term)
Logging in
To authenticate with pianod2
, issue:
USER {username} {password}
This JSON equivalent is the authenticate
request:
{"authenticate":{"username":"some_name","password":"the_password"}}
When the session is complete:
QUIT
The JSON equivalent is the disconnect
request:
{"disconnect":{}}
When scripting pianod2
, A single command can be
issued via:
AS USER {user} {password} {command}
This authenticates as the specified user, executes the command,
and closes the connection. (WAIT
commands are
respected, and close when the waited-on event occurs.) The AS
USER
form does not broadcast user login/logout or effect
autotuning. This is intended to ease implementation of simpler
clients and automation systems that do not hold connections
open.
A freshly installed pianod2
system will have a
single user ‘admin’ with password
‘admin’.
After authenticating, a user can change their password (JSON:
setUserPassword
):
SET PASSWORD {old} {new}
Help
Help is displayed based on user rank and privileges; listeners will see only listener-only commands; administrators will see the full list. Specifying a command lists matching commands only.
HELP [{command}] ...
For equivalent JSON requests, use the SCHEMA command (JSON:
getSchema
):
SCHEMA [{request}] ...
Without a request name, all requests are listed. Request names must be complete and, unlike most of command-line mode, they are case-sensitive. See the JSON requests documentation for more information.
Sources
pianod2
supports multiple sources. A source can
be:
- A Pandora account
- The built-in tone generator
- A local media collection (i.e., mp3 files)
- Other future sources
- Pianod2’s media manager
Each pianod2
session has a current source. At
login, the source is the media manager, assigned source #1, which
aggregates all other sources’ playlists and collections.
When another source is created, it is assigned an ID which can
be used to select that source (JSON:
selectSource
):
SOURCE SELECT ID {#id}
Sources can also be selected by type (JSON: also
selectSource
):
SOURCE SELECT TYPE {type} NAME {name}
type describes the kind of source, such as
Pandora
or tonegenerator
. name
may be specified when a source is created, but usually defaults to
the pianod username.
To view the current sources:
SOURCE LIST <ENABLED|AVAILABLE|MINE|TYPE>
Lists sources in use, those with stored credentials (which may or may not presently be in use), those owned by the user, and the types compiled into pianod.
JSON equivalents are: getSourcesEnabled
,
getSourcesAvailable
, getMySources
, and
getSourceTypes
.
Removing is broken into two aspects, disconnect and forgetting.
Disconnecting sources parallels selecting (JSON:
disconnectSource
):
SOURCE DISCONNECT ID {#id}
SOURCE DISCONNECT TYPE {type} NAME {name}
This disconnects an active source, but if remembered or restored
(see standard options below) its parameters are retained. These
parameters may be removed by (JSON: forgetSource
):
SOURCE FORGET TYPE {name} NAME {name}
Forgetting removes stored parameters, but does not disconnect the source.
If a source is busy (currently playing a song), its removal is processed but deferred until it is free.
A single command may be executed with a different source:
WITH SOURCE ID {id} {command} …
WITH SOURCE TYPE {type} NAME {name} {command} …
Standard Source Options
There are some standard options when creating any source:
REMEMBER
- Persist the source options. For security, options are stored
with the creating user’s data, not the owning user.
Passwords are enciphered to prevent casual reading but are not
stored in a cryptographically secure manner. May not be used with
OWNER
. OWNED BY {user}
- Assigns ownership of the source to the specified user. May not
be used with
REMEMBER
. WAIT
- Waits for the source to become ready. Depending on the source and conditions, this may be some time.
NAME {name}
- Assigns a name; the default is the user’s name. The source type and name can be used to select sources. The type/name pair must be unique to add a source. A remembered source will overwrite an existing one with that type and name.
ACCESS <DISOWNED|PRIVATE|SHARED|PUBLISHED|PUBLIC>
- Set access for the source.
DISOWNED
: Nobody can manipulate the source.PRIVATE
: Only the owner may use or modify the source.SHARED
: Anyone may use the source, but only the owner can review details or modify it.PUBLISHED
: Anyone may use or review the source, but only the owner may modify it.PUBLIC
: Anyone may use, review, or modify the source. SONG PROXY {proxy-mode}
- proxy-mode may be
DONOR
,RECIPIENT
, orNONE
. When playing a song from a proxy recipient, pianod checks proxy donors for a matching item. If found, the donor item is transparently substituted. Donors incapable of requests are ignored.
Pandora Sources
Administrators and those with service
privilege can
add a Pandora source (JSON: createPandoraSource
):
PANDORA USER {username} {password} [{connection options}] …
In addition to standard source options, the Pandora source supports:
ACCOUNT TYPE <AUTOMATIC|STANDARD|PLUS|PREMIUM>
- Default is
AUTOMATIC
, which adjusts connection parameters based on the account type reported by Pandora. PROXY {url}
- Set a proxy.
CONTROL PROXY {url}
- Set a control proxy, typically for those outside the USA. For information on proxies, see The Onion Router (tor).
PAUSE TIMEOUT {#duration:15-86400}
- When paused this duration (in seconds), playback is cancelled.
PLAYLIST TIMEOUT {#duration:1800-86400}
- Playlist items expire this number of seconds after retrieval.
CACHE MINIMUM {#minimum:800-989999} MAXIMUM {#maximum:1000-999999}
- Number of songs/artists/albums to retain in the cache.
More rarely used are:
RPC HOST {hostname}
- Set the Pandora RPC hostname and optional port number.
PARTNER {user} {password}
- Set the Pandora Partner username and password.
PARTNER DEVICE {device_type}
- Choose the reported device type.
ENCRYPTION PASSWORD {password}
- Set the enciphering password for data sent to the server.
DECRYPTION PASSWORD {password}
- Set the deciphering password for data sent by the server.
To view a Pandora source’s parameters, select that source and:
PANDORA SETTINGS
Filesystem Source
The filesystem source uses a media collection from a local hard
drive or mounted network drive/share (JSON:
createFilesystemSource
):
FILESYSTEM ADD {path} [{options}] …
Path must be a fully-qualified path to a directory where media is located. Subdirectories are recursed to find media, but symbolic links are not followed. If adding a large collection, the initial add may take a while.
In addition to standard parameters, filesystem sources support:
RESCAN <ONCE|ALWAYS|NEVER|PERIODICALLY>
- Whether to rescan the filesystem media files on load. In addition to adding new media and removing missing media, the catalog is updated to reflect metadata updates in the files. An initial scan is done regardless of this setting. The source does not become ready until an on-load scan is complete. However, periodic scans are performed on the live source. The period is currently each day.
RATINGS BIAS {1-100}
- Controls amount of selection bias applied based on ratings. When set to 1, track selection is not effected by ratings. At 100, a top rating is preferred 10:1 over neutral, while a bottom rating preferred 1/20th a neutral-rated track.
RECENT BIAS {0-100}
- Controls selection bias based on last play. At 1, no bias is applied; at 100, the chance assigned each song is roughly linear to the time since last play.
Rescanning for new media can also be requested on an active
filesystem source. Use SOURCE SELECT
or WITH
SOURCE
to choose a filesystem source, then (JSON:
rescanFilesystem
):
FILESYSTEM RESCAN [FRESH]
The source remains available during rescan. Fresh rescans trash the existing index and rebuild. Playlists are retained. This fixes some problems (such as albums erroneously marked as compilations because of bad metadata, which has since been corrected), but risks changing the IDs assigned some media, effecting seeds and ratings.
Tone generator Source
The tone generator is used for audio testing, troubleshooting
and debugging (JSON: createTonegeneratorSource
):
TONE GENERATOR ACTIVATE [{options}] …
The tone generator includes 1KHz and 440Hz “A” test tones, all four Westminster quarters, a variety of telephone tones and the legacy EBS tone for when you really want people to go home at the end of the party. (The Intergalactic Computer Distress Signal may also serve this role effectively.)
Restoring from a persisted source/credentials
To use persisted source credentials:
{source type} USE {name} {connection options} …
The JSON equivalents are usePandoraSource
,
useFilesystemSource
, and
useTonegeneratorSource
.
name is the instance name given when originally created. Default naming varies by source type.
Rooms
Pianod2 supports multiple output devices, each with its own
sources, music, and other controls. Each output device is a
pianod2
“room”, each room represents a zone in home
automation scenario. The initial room is named pianod
,
and newly connected users start in that room. To create a new
room:
ROOM CREATE {roomname} [{audio_options}] …
See Audio Configuration for more information on audio options.
To view available rooms:
ROOMS LIST
To switch rooms:
ROOM SELECT {roomname}
The current room’s audio options may be changed:
ROOM RECONFIGURE [{audio options}] …
To remove rooms:
ROOM DELETE {roomname} [NOW]
roomname may not be the initial room
(pianod
). A song in play is allowed to complete,
unless NOW
is specified. Users in the room are logged
out when the room is removed.
Status commands
Status commands are available to all users.
The “null” command is done by sending an empty line. This reports the status of playback and the playback time within the current song, if applicable.
Other status commands are:
STATUS
Status indicates the current song, if there is one. JSON:
getStatus
. It also causes player status, selected
playlist and source, and current room to be announced (though these
are outside the data response).
HISTORY LIST [{index}]
QUEUE LIST [{index}]
HISTORY LIST
returns a list of items previously
played, up to the history size limit, or the specific history item
if an index is specified. JSON:
getHistory
QUEUE LIST
does the same for upcoming songs. JSON:
getQueue. The queue/playlist replenishes periodically when it it
has become empty. The random queue may contain songs which are
never played if their playlist is no longer applicable when they
reach the front of the queue; in this case, they simply disappear.
Furthermore, requests are always queued before any randomly chosen
selections.
Both HISTORY
and QUEUE
allow an index
to be specified. If 0, the current song is returned. For negative
indices, HISTORY
refers to upcoming tracks and
QUEUE
to previously played tracks.
Playlist Selection, Queue Mode & Playback Control
The list of playlists is available to all users (JSON:
getPlaylists
):
PLAYLIST LIST {predicate}
The short form PLAYLIST
is also accepted, but
future behavior is not guaranteed; currently it generates a shorter
variant. If no predicate is given, all playlists are listed.
Playback control consists of 3 mechanisms:
- Queue mode, which is either stopped, requests only, or random play
- Which playlist to make selections from, if in random play
- Playback state, either paused or playing
These are all controlled with the PLAY
and
SELECT
commands (JSON: play
and
select
. SELECT
changes any of these
without affecting others. PLAY
, unless directed
otherwise, sets the queue mode to random play and resumes
playing.
Playlist selection is available to standard and administrative users. A playlist may be selected by:
SELECT <AUTO | MIX | EVERYTHING>
SELECT PLAYLIST <ID | NAME | LIKE> {playlist}
SELECT FROM {predicate}
MIX
plays music from playlists currently “in the mix” (see the MIX command).AUTO
uses the mix, but enables Automatic Playlist Selection (Autotuning).EVERYTHING
plays music from all playlists and libraries.PLAYLIST
plays the single playlist specified by the predicate.FROM
plays music selected by the predicate.
If the current source is #1 (media manager), MIX
and EVERYTHING
source from all applicable
playlists/collections of all sources. When another source is
selected, only ithat source’s playlists apply.
Queue Mode is controlled by:
PLAY [RANDOM | REQUEST]
PLAY STOP [NOW]
Setting the queue mode to STOP
prevents cueing of
new tracks, but any currently playing track finished out unless
NOW
is specified. STOP NOW
behaves
similar to SKIP
.
Playback controls are:
SELECT PAUSE
PLAY TOGGLE
SELECT RESUME
PAUSE
pauses (or toggles) playback.
RESUME
resumes a paused player, but does not alter the
queue mode.
Queue mode, playback, and/or playlist selection can be combined:
SELECT RESUME RANDOM PLAYLIST NAME "THOMAS DOLBY"
PLAY RANDOM TOGGLE AUTO
Other Controls
To skip the remainder of the current song (JSON:
skip
):
SKIP
In attempting to respect licenses, skips are limited by some sources. Pandora, for example, limits to 6 skips/station (playlist)/hour.
The playback volume can be adjusted with VOLUME
.
The natural decoding level is at level 0dB. Positive numbers
increase volume (and generally introduce distortion), negative
numbers decrease the volume. The range is ±100, but between -40 and
0 is practical.
To retrieve the volume level (JSON: getVolume
):
VOLUME
To set the volume level (JSON: setVolume
):
VOLUME LEVEL {#level}
To adjust the volume level (JSON:
adjustVolume
):
VOLUME <UP|DOWN> [{#change}]
change defaults to 1.
Crossfading can be adjusted by:
CROSSFADE DURATION [{#seconds}]
CROSSFADE LEVEL [{#level}]
Leaving off the value retrieves current values.
For JSON, use instead setCrossfadeTime
,
setCrossfadeLevel
, getCrossfadeTime
, and
getCrossfadeLevel
.
Manipulating the Mix (Shuffle/QuickMix)
MIX
refers to mixing playlists. The mix is composed
of 1 or more playlists, with songs chosen at random among the
playlists in the mix. The MIX command displays and selects
participating playlists (JSON: getMix
,
getMixExclusions
):
MIX LIST INCLUDED
MIX LIST EXCLUDED
These commands output playlists in (not in) the mix. The short
forms MIX
, MIX LIST
, MIX
INCLUDED
and MIX EXCLUDED
are accepted but
their future behavior is not guaranteed.
To set or revise the playlists in the mix (JSON: adjustMix):
MIX <SET | ADD | REMOVE | TOGGLE> {predicate}
Choosing selection method
The queue is repopulated with a few random selections (usually
4) at a time when needed. The active playlist (or the playlists
encompassed for the metaplaylist MIX and EVERYTHING) always
influences choices. However, There are several manners available,
set via (JSON: setRandomizeMethod
):
QUEUE RANDOMIZE BY <SONG | ALBUM | ARTIST | PLAYLIST | RANDOM>
These function as follows:
SONG
- Picks 4 songs at random. If the active playlist is a metaplaylist, they are chosen from all its enabled playlists. For the manager’s metaplaylists, 4 songs are queued from each source.
ARTIST
- If supported by the source, randomly chooses an applicable
artist and queues 4 random songs. If unsupported, behaves like
SONG
. ALBUM
- If supported, choses an applicable album and queues the entire
thing, in track order. If unsupported, behaves like
ARTIST
. PLAYLIST
- Always supported. Picks 4 songs at random. If the active playlist is a metaplaylist, the 4 songs will come from a single playlist.
RANDOM
- One of the aforementioned modes is chosen at random and used to fill the queue.
Finding music
There are two ways to find music:
FIND [manner] {predicate}
The format is similar to that of track information. JSON:
getSuggestions
. Results may include mixed types; an
expression can use TYPE={type}
in a filter expression
to restrict returned types. In the LIKE form, specifying a type
indicates which field to search on, but does not restrict the
returned type. Results are suitable for seeding. Other actions
may be available, depending on the source the item
originated from; the action field for each song indicates
additional capabilities.
manner is one of:
- SUGGESTION
- Shallow search (default). If an artist matches, omit individual albums and songs; if an album matches, exclude individual songs.
- ALL
- Search exhaustively. If an artist matches, include their albums and songs too.
- REQUEST
- Search exhaustively, but only search sources that allow requests.
Commands are:
SONG LIST {predicate}
Returns songs for request. Other actions may be available, depending on the source the item originated from; the action field for songs will indicate additional capabilities.
PLAYLIST SONG LIST {playlist predicate}
List all songs in a given playlist.
Track Requests & Queue management
For sources which support requesting songs (JSON:
request
):
REQUEST {predicate}
For NAME
and ID
predicates, items are
queued in the order listed. The predicate searches for songs (as
does SONG LIST); however, if ID
predicates are used,
they may specify an artist or album. An album ID requests the
album’s tracks in album order; an artist ID requests all albums in
an unspecified order, but each albums’ tracks will be in album
order.
Users rank can clear the request queue (JSON:
clearRequests
):
REQUEST CLEAR
The random queue cannot be cleared.
To remove requests in either queue (JSON:
cancelRequests
):
REQUEST CANCEL {predicate}
Predicate applies to the songs in the queues. Skip limiting applies, if applicable to the songs’ sources.
Music Control: Ratings & Seeds
Ratings
To rate songs or playlists (JSON: rateTrack
and
ratePlaylist
):
RATE <SONG | PLAYLIST> {rating} {predicate}
RATE
adjusts the song or playlists’s user
preference. Depending on these source, these are maintained by
pianod2
or adjusted by the source’s server.
rating may be a rating adjective (see earlier) or a number in the range 0.5–5.0. Values not supported by the source are rounded if it is sane to do so.
For songs, an additional rating of OVERPLAYED
has
source-specific effects. For example, Pandora banishes the song for
30 days; local media reduces its play likelihood temporarily and
prevents it being requested more than once per day.
If predicate is not specified, the rating applies to the present track (even if paused) or playlist.
Seeds
Adding, removing and toggling seeds via a song is done via:
SEED {verb} [type {type}] [{seeds_predicate}] [TO PLAYLIST {playlist_predicate}]
PLAYLIST MODIFY {playlist_predicate} {verb} SEED {verb} {seeds_predicate}
Where:
- verb is
add
,delete
ortoggle
- seeds_predicate specifies the songs, artists, albums or playlists to add or remove as seeds; if omitted, uses the current song. Not all sources support all seed types.
- playlist_predicate specifies playlists to apply the change to; if omitted, uses the current playlist/current track’s playlist (which must not conflict).
The two forms vary to allow both complex playlist and seed
predicates. JSON has a single request, alterSeeds
, in
which both predicates can be complex.
Reviewing Seeds
To view seeds or feedback for a playlist (JSON:
getSeeds
):
SEED LIST [PLAYLIST <ID | NAME | LIKE> {playlist}]
If the predicate is omitted, the selected playlist is used if it
is not a metaplaylist. This command returns a list of all seeds
and, for some sources, ratings associated with the playlist. The
format is a subset of that used for track information, with the
returned fields varying depending on whether an item is a rating,
song seed, album seed, artist seed, or playlist seed. Use the
previously discussed SEED DELETE
or RATE
NEUTRAL
commands to remove seeds or ratings.
Playlists
To create a new playlist (JSON:
createPlaylist
):
PLAYLIST CREATE [SMART] [NAME {name}] FROM {predicate}
The items selected by the predicate are added as the first seeds
of the new playlist. If omitted, the current song is used. The
default name varies; Pandora, for example, uses the song or artist
name with " Radio" appended. If creating a
SMART
playlist, the predicate is converted to an
expression (if necessary) and embedded in the playlist. The
playlist will include songs matched by the expression, although
these are not considered seeds. Additional seeds may be added to
the playlist. Removing seeds will not remove them from the playlist
if they match the expression.
PLAYLIST CREATE NAME {name} WHERE {expression}
Creates a new smart playlist (JSON:
createPlaylistFromFilter
). The expression becomes part
of the playlist, which will include songs matched by the
expression, although these are not considered seeds. Additional
seeds may be added to the playlist. Removing seeds will not remove
them from the playlist if they match the expression.
This is rather similar to PLAYLIST CREATE SMART NAME
{name} FROM ...
however, that version can accept any
predicate type, provides a name if one is not given, and ensures
there are matching results before creation. This version is used
heavily by unit test scripts.
Note similarity to PLAYLIST CREATE NAME {name} FROM WHERE
{expression}
, which will create a regular playlist with the
matched items as starter seeds, rather than a smart playlist.
Renaming a playlist (JSON: renamePlaylist
):
RENAME PLAYLIST <ID | NAME | LIKE> {playlist} TO {new name}
Deleting a playlist (JSON: deletePlaylist
):
DELETE PLAYLIST <ID | NAME | LIKE> {playlist}
Automatic Playlist Selection (Autotuning)
With autotuning, pianod2
automatically selects
QuickMix playlists based on who is listening. There are 3 ways
pianod2
can assess who is listening:
- Current authenticated users
- I.e., users connected and authenticated at present. This can work well in environments where listeners stay connected, such as having a client open on their desktop during work.
- A list of users set via the
AUTOTUNE
command - This method is appropriate when users will not not stay
connected. For example, if listeners will be intermittently
accessing
pianod2
via their mobile device. In this scenario, a external helper program (such as Proximity) will update the listener list. - The superset of the two previous methods
- This method combines the previous two methods.
User sets their playlist preferences with the aforementioned
RATE
command. Playlist preferences are persisted with
user data; visitors can not create playlist preferences. Existing
users can be excluded from playlist calculations by revoking
influence
privilege.
Autotuning is enabled by:
PLAY AUTO
pianod2
tries the following successively, trying to
find the best selection for the current listeners. To do this,
pianod:
- Outright omits any playlists any current listener has rated 2 (bad) or less.
- Calculates average ratings for the remaining playlists, considering preferences of the current users.
- Omits any playlists with an average rating of less than 3 (neutral).
- Select for play all playlists within 1 star of the top rating. If no playlists apply, be quiet.
In the future, step 4 may be adjusted to play from all remaining playlists, but bias selections proportionally to rating.
Pianod selections are made when autotuning is first enabled and subsequently when a user authenticates or disconnects. Playlist selections can be adjusted manually but will be overwritten as listeners change unless autotuning is disabled.
If no users with influence
are connected,
pianod2
will play all playlists. If no users at all
are connected, autotuning will pause between tracks. Music will
automatically resume when a users logs in.
Note that if you have some automated process that will start
music via the AS USER PLAY AUTO
command, it is best to
REVOKE INFLUENCE
that user.
Configuring Autotuning
The method of autotuning is selected by an administrator (JSON:
setAutotuneMode
):
AUTOTUNE MODE <LOGIN | FLAG | ALL | settings> …
LOGIN
looks at users in a room. FLAG
look at a user attribute flag; see below. Multiple modes may be
specified; ALL
is a shorthand for LOGIN
FLAG
. If omitted, the mode is unchanged. Regardless of the
mode, a user must have INFLUENCE
privilege to
be considered.
Administrators or others with tuner
privilege can
select the users considered by FLAG
mode:
AUTOTUNE FOR ...
AUTOTUNE CONSIDER <user> ...
AUTOTUNE DISREGARD <user> ...
FOR
specifies a user list; if empty, there are no
listeners. CONSIDER
and DISREGARD
add and
remove users to/from the list. This use effects all rooms.
AUTOTUNE LIST USERS
Lists users currently considered by the autotuning algorithm. That is, users who have influence privilege and are either or logged in or flagged present in accordance with the autotuning mode.
The following settings are available for autotuning:
VETO {rating}
- Rejects a playlist if any individual user rating is less than that specified.
REJECT {rating}
- Rejects a playlist if the average user rating is less than that specified.
INCLUDE {rating}
- Includes all playlists of the specified rating or better.
QUANTITY GOAL {#count}
- Sets a goal for number of playlists to include. If this quantity is not met by playlists meeting the include rating, then additional playlists that are neither vetoed nor rejected will be added to make up the difference, subject to the quality margin. Setting count to 0 will disable this behavior. The idea here is to set quality margins as high as possible, but be willing to sacrifice some in the name of variety.
QUALITY MARGIN {#margin}
- Quality margin is used to create a sort of dynamic rejection rating, which is equal to the best average playlist rating - the margin. Used in concert with the quantity goal, this allows playlists below the include rating to be included for the sake of variety.
User Maintenance
User Security Model
User abilities consist of a rank and privileges, and are
indicated at connection, authentication, and whenever they
subsequently change. They can also be retrieved via the GET
PRIVILEGES
command.
There are 4 ranks of user: disabled, listener, standard, and administrator. Each rank has all the abilities of the lower ranks plus the ones added for that rank. The ranks have the following abilities:
- disabled
- No permissions to anything. However, events are still broadcast to the user.
- listener
- Can monitor playback, check the queue and history, and yell but not effect playback behavior in any way. (However, a listener with influence privilege will influence autotuner picks.)
- standard
- Adds control playback (play, pause, volume adjustment, playlist selection).
- administrator
- Adds network parameter control, creating and adjusting users, etc.
In addition to the ranks, there are privileges; privileges are independent of each other and rank, except that disabled rank disables all privileges too. The privileges are:
- service
- The user can change the Pandora account. All administrators gain this privilege too.
- deejay
- The user can request music and cancel songs that have been queued. Standard rank includes these privileges and far more.
- influence
- When autotuning, the user’s playlist ratings are considered if the user is logged in or flagged as present.
- tuner
- The user can set the listeners considered by the autotuning
algorithm when in
flag
orall
mode. All administrators have this privilege too.
By default, unauthenticated users (“visitors”) have
listener privileges, but this can be adjusted via SET VISITOR
RANK
. Visitors have no privileges.
User Maintenance Commands
These administrator commands set the ranks and privileges of those using the server.
Creating users (JSON: createUser
):
CREATE <LISTENER|USER|ADMIN> {name} {password}
Resetting a password (JSON: setUserPassword
):
SET USER PASSWORD {username} {password}
Viewing users (JSON: getUserList
,
getUsersOnline
, getUserByPrivilege
):
USERS LIST [{username}]
USERS ONLINE
USERS WITH <OWNER|SERVICE|INFLUENCE|TUNER|PRESENT>
Note that if social actions are enabled, USERS
ONLINE
is available to all users. However, only
administrators can view user privilege details.
Changing ranks and privileges (JSON:
setVisitorRank
, getVisitorRank
,
grantUserPrivilege
, and
revokeUserPrivilege
):
SET VISITOR RANK <DISABLED|LISTENER|USER|ADMIN>
SET USER RANK {user} <DISABLED|LISTENER|USER|ADMIN>
GRANT {privilege} TO {user} ...
REVOKE {privilege} FROM {user} ...
Rank changes are effective immediately, with privilege change messages sent when necessary.
Removing a user account (JSON: deleteUser
):
DELETE USER {username}
Note you can not delete a user that is presently logged in. However, you can:
KICK [ALL|ROOM] USER < user > [{message}]
KICK [ALL|ROOM] VISITORS [{message}]
These terminate all sessions for the user or all visitors,
respectively. JSON: logoffUsers
and
logoffVisitors
. However, since a misbehaving user is
likely to reconnect, it may be more useful to set their rank to
disabled.
When ROOM is specified, only sessions in the current room are terminated. If ALL is specified, or by default, termination applies to all rooms.
Shadowing
If enabled, pianod user accounts are created on-demand for system user users, initially using their system password. If a shadowed user changes their password within pianod, it applies only to pianod and their system password is unchanged.
Password shadowing may be restored (JSON:
switchToSystemPassword
):
SET SHADOW PASSWORD {pianod} {system}
Shadow users by default are disabled and have no privileges. To
set some other set of privileges, choose an existing user or create
a template user (JSON: setShadowUserName
):
SET SHADOW USER NAME {username}
If username is empty, it dissociates the template user. You can
check the current template (JSON:
getShadowUserName
):
GET SHADOW USER NAME
Waiting for asynchronous events
To wait for a new source to be ready, include the
WAIT
option in the new source parameters.
To wait for a song to complete, use (JSON:
waitForEndOfTrack
):
WAIT FOR END OF [CURRENT] SONG [{options}]
If CURRENT
is specified, a song must be playing or
it is an error. Without CURRENT
, the command applies
to the current song, or the next song if nothing is playing.
To wait for the next song to play (JSON:
waitForNextTrack
):
WAIT FOR NEXT SONG [{options}]
This can be used even if the play is paused or stopped.
To wait for sources to become ready:
WAIT FOR SOURCE ALL [PENDING] READY [{options}]
WAIT FOR SOURCE ANY [PENDING] READY [{options}]
WAIT FOR SOURCE ID {id} READY [{options}]
WAIT FOR SOURCE TYPE {type} NAME {name} READY [{options}]
If PENDING
is used, there must be sources currently
initializing or it is an error. (See also the WAIT
source option.) JSON requests are waitForSourcesReady
and waitForSpecifiedSourceReady
.
Options are:
TIMEOUT {#seconds}
- Fail if the event does not happen within the specified time.
Miscellaneous Controls
Comment always succeeds:
# Some text here
Note there must be a space after the hash, so:
#This is an error.
Administrators can set the number of songs that are retained by the history:
SET HISTORY LENGTH {number}
Note that pianod2
keeps a lot of metadata (like
playlist seeds) around for these songs. Although the data is cached
several hours, keeping history to a sane length is a good idea.
To broadcast a message to other sessions:
YELL {something}
There is no way to do a directed messages; it’s a music server, not an IM client! Available to all ranks except disabled.
This administrator setting enables or disables this social behavior; default is on:
ANNOUNCE USER ACTIONS <ON|OFF>
When on, pianod2
shares significant events and user
actions by broadcasting a message and identifying who originated
them. This setting also controls the USERS ONLINE
command availability.
Administrators can set which messages are logged to standard out
(JSON: setLoggingFlags
):
SET [FOOTBALL] LOGGING FLAGS {value}
In increasing order of detail: 0x1000 (aforementioned user
actions & errors), 0xfef8, 0xfefc, 0xfefe, and 0xffff
(everything) are useful. See logging.h
for specific
flags. Note, however, that 0x1000 creates security concerns as it
will log user passwords.
User rank and privileges can be retrieved by a session (JSON:
getPrivileges
):
GET PRIVILEGES
This administrator command flushes dirty buffers, including user
data and source data (JSON: sync
):
SYNC
This is mostly for test, but can be used if pianod refuses to shutdown gracefully. Under normal operation, data is periodically written by pianod; more important changes promote faster writes.
Administrators can shutdown the server (JSON:
shutdown
):
SHUTDOWN
Following shutdown, each room is removed as its playback
completes, with shutdown proceeding when the last room has been
dissolved. To stop immediately, use STOP NOW
to abort
playback. Signals are also viable shutdown mechanisms;
pianod2
recognizes SIGHUP, SIGTERM, and SIGINT as
requests to shutdown immediately but gracefully. SIGKILL (-9) will
kill pianod2
without shutdown; recent user changes,
preferences and ratings may be lost.
Predicates
Several commands use a standard predicate form:
[manner] [type] <ID | NAME | LIKE | WHERE> {specifier}
[manner] [type] SOURCE ID {id} <ID | NAME | LIKE | WHERE> {specifier}
[manner] [type] SOURCE TYPE {type} NAME {name} <ID | NAME | LIKE | WHERE> {specifier}
Where
manner :== [AUTHORITATIVE | DISCRETIONARY]
type :== [ANY | ARTIST | ALBUM | SONG | PLAYLIST | GENRE]
Predicates accept item lists (ID
,
NAME
) or are patterns that may match multiple items
(LIKE
, WHERE
).
type
does not apply if type is implied by the
command. At present, it only applies to LIKE; if omitted when
accepted, it is equivalent to ANY.
The predicate types are as follow:
ID
- The predicate is a list of IDs of a song, album, artist or playlist.
NAME
- The predicate is a list of names of a song, album, artist or playlist (as appropriate for the command).
LIKE
- The predicate is a list of phrases. Each is split into words,
and matches songs, albums, artists or playlists (as appropriate for
the command or as specified by
type
) which contain all the words in the predicate. WHERE
- The predicate is an expression which matches songs, albums, artists or playlists (as appropriate for the command) in accordance with the expression. See the filter grammar for details.
SOURCE
- Allows a specific source to be specified for fulfilling a predicate.
manner
has meaning when interpreting predicates
against the media manager source.
AUTHORITATIVE
- Require all sources execute the query successfully. If any source cannot perform a query because the query exceeds its ability, the command fails.
DISCRETIONARY
- Search sources that are capable of the query. If a source can’t perform a query, that source is ignored and results gathered from other sources.
If a source encounters an error (as opposed to a limitation), a discretionary search will still fail.
Infix vs. Suffix
Predicates may occur infix (in the middle of a command line) or suffix (at the end). Suffix predicates allow multiple parameters. Infix predicates, however, accept only one parameter as their parameter.
Furthermore, filter expressions (predicate form
WHERE
) are outside the standard Football/pianod
command line parsing. The usual word boundary and quoting rules are
not applicable, preventing WHERE predicates from infix use
entirely.
Playlists vs. Others
- Playlist predicates NAME, LIKE, and WHERE search playlist names only. Playlist ID predicates can accept playlist IDs or song IDs. A song’s playlist will be used if a song is given; it is an error if the song does not have a playlist.
- Other predicates search albums, artists, songs and genres/playlists.
Efficiency
IDs are encoded with source, type, and a unique item identifier.
Items can be retrieved by ID more quickly than via other methods; a
decoded ID is routed to the correct source, which in turn probably
uses hash tables. This gain only applies to ID predicate
form (such as ID "3ss3578329"
); building an
expression that uses ID (such as WHERE ID =
"3ss3578329"
) will function but, like most
predicates, locate items by exhaustive comparison. Since the ID is
unique, it guarantees the correct item is manipulated, and no
side-effects to similar items.
Additionally, ID predicates are validated to ensure each item exists. With other predicate forms, lack of a match is not an error; for IDs, it is an error if a corresponding item is not found. If multiple IDs were specified, none are processed.
Thus, ID
predicates are the “gold standard,” and
should be used by client implementations whenever possible.
Filter Mechanism
Filter expressions are used with the WHERE
predicate form.
History & Thanks
I originally developed the filter in late 2005 as a
replacement for the mserv filter mechanism. It was
backward-compatible with the original mserv filter, with new
features and a performance boost. pianod2
retains a
similar syntax but includes various enhancements.
My thanks to Kimmo Suominen for documenting the original mserv filter syntax.
This document describes the pianod2
filter
syntax.
Overview
- The filter parses the command line once to build a parse tree that is utilized to evaluating each song, artist, album, or playlist.
- When matching, a filter uses short-circuit logic to improve performance.
- Operators include substring searches, exact matches, and regular expressions and greater/less than on several fields.
- There is a “search” field which matches on both artist, title, and album name fields; for playlists, the playlist name is matched on.
- When comparing, “A”, “An” and ”The " are skipped at the start of songs, so “request where author=beatles" will match both “Beatles” and “The Beatles”. A ‘*’ at the end of a string (but not the middle) acts as a wildcard.
- Filter expressions may be arbitrarily long and complex.
- Whitespace is allowed, allowing expressions to be more legible.
Operators and Precedence
- () (highest)
- comparison operators: = == != < > <= >= =~ “quoted text”
- ! (negation)
- | or || (binary or)
- & or && (binary and) (lowest)
Evaluation is left to right.
- Operator =~
- Performs substring search operator (strings fields only).
- Operator = and Operator ==
- Both = and == are equivalence operators.
- Operator !=
- Inequivalence operator.
- Operator <, >, <= and >=
- Perform strings or numeric comparison appropriate to the data field.
- Operator :
- Not implemented yet. Perform regular expression matching.
- “Quoted text”
- Equivalent to SEARCH =~ “the text enclosed”. Use either single quotes (apostrophes) or double quotes.
The RHS of comparisons can be quoted. If the next non-whitespace character after the operator is a single or double quote, then the string is collected up to the close quote, which must be the same type as the open quote. The quote character may be inserted into the string by doubling it in place.
Ratings: superb, good, neutral, bad, awful, several other adjectives or a number between 0.5 and 5.0.
Comparisons/Keywords
Binary
- HEARD (songs)
- True if the song has ever been played. Not all sources persist this information.
- RATED (songs, playlists)
- True if the song or playlist has been rated by any user, logged in or not.
- PLAYED
- True if the song has ever been played. Equivalent to LASTPLAY > 0.
- COMPILATION
- True if an album is a compilation or a song is from a compilation album.
- FALSE
- False is always false. Useful in creating expressions that return the empty set.
String
- ID = {id}
- True if the ID matches. No wildcards.
- TYPE = type
- Match only the specified type, which is one of: ARTIST or AUTHOR, ALBUM or ALBUMNAME, TRACK or TITLE or SONG.
- ALBUM {cmp} pattern (also ALBUMNAME; applies to albums, tracks)
- Compare album name. False for artists or playlists.
- ARTIST {cmp} pattern (also AUTHOR; albums, songs)
- Compare artist name. False for playlists.
- TITLE {cmp} pattern (also SONG; tracks, playlists)
- Compare song/track title. False for artists, songs or playlists.
- PLAYLIST {cmp} pattern
- Compare playlist name. False for artists and albums.
- NAME {cmp} pattern
- Compare primary field. For songs, compares titles; for albums, album name; for artists, the artist name; for playlists, the playlist name.
- SEARCH [ = =~ ] pattern (all)
- Compare the various components to the pattern. {cmp} can be any of the comparison operators. SEARCH checks artist name, album names, track title and and playlist name, and can not be used with less than/greater than.
- GENRE [ = =~ ] {genre} (tracks, playlists)
- True if a song or playlist belongs to
genre
. No wildcards, but =~ does a substring match rather than matching genre name - so for example, you could use GENRE =~ “ROCK/POP”. When using operator =, genres may be separated by commas, slashes and plus (common on FreeDb and CDDB). - {user} {cmp} {rating} (songs, playlists)
- Compare the user’s rating for a song or playlist.
- {user} = RATED
- True if specified user has rated the item.
Numeric
- RATING {cmp} number
- Compare the average rating of a song or playlist.
- DURATION {cmp} number
- Compare duration of song, in seconds.
- LASTPLAY {cmp} number
- Compare last played time, in hours.
- TRACK {cmp} number
- Compare track number.
- YEAR {cmp} number
- Compare year.
Unknown values vs. Nonexistent fields
When values are unknown, the comparison returns false.
For example, year > 1955
returns one set of songs,
year <= 1955
returns a second, but there is a third
set of songs without any assigned year. These can be found with
some finagling, such as !(year >= 1).
If a value does not exist, it is always false, even
with preceding negation. For example a search for year >
1955 || year <= 1955 || !year >= 1
on an
artist is false, because artists do not have years. However, a
filter for year > 1955 | “Madonna”
could be true,
because the non sequitur portion of the expression does not
preclude the rest being potentially true.
Audio Configuration
The ROOM CREATE
and ROOM RECONFIGURE
commands provide options to redirect or reconfigure output. Audio
options are as follows:
LIBRARY {library}
-
Select an output library. Unless specifically configured otherwise, you will only have one output library. When using Mac OS X’s AVFoundation as the media engine, this setting is ignored. Valid values (assuming they are compiled in) are:
libao
libsdl
libavdevice
(part of ffmpeg)gstreamer
DRIVER {driver}
-
Select a driver. See the various libraries’ documentation for values.
libao
: Selects libao drivers.libavdevice
: Selects libavdevice outputs or libavdevice protocols.
DEVICE {device}
-
Specify a device. See the various libraries’ documentation for values.
libao
: Specifies a device to use.gstreamer
: Select gstreamer plug-ins for output using the grammar allowed by gst-launch. May describe a pipeline.libsdl
: Selects libsdl outputs.
ID {#id}
- For libao, to specify a libao output by number.
OPTIONS {options}
-
Other options for the audio device. See the various libraries’ documentation for values.
libavdevice
: A string with option name-value pairs applied when opening the driver.
SERVER {server}
-
Destination and/or parameters applicable when an audio server driver is in use.
libao
: Used by streaming media drivers to specify a destination.libavdevice
: Passed to the library as a filename when opening the output.
VOLUME {level}
- The initial volume level of the room. Defaults 0dB gain.
CROSSFADE DURATION {seconds}
- The duration of crossfade (overlap) between songs. Defaults to 0, in which case songs are not cross-faded. Cross-fading helps reduce the sense of breaks between songs, but used wrong can be annoying; violin concertos mixing can sound like cats in heat. Fast-paced songs seem to do well with 2–2.5 seconds, classical either 0 or 5 seconds, soft rock 5–10 seconds.
CROSSFADE LEVEL {level}
- The amount of volume adjustment to apply when crossfading, in
decibels. This is a positive number; the default is 10dB. The
cueing song is faded from
current_volume - crossfade_level
tocurrent_volume
; the ending song concurrently undergoes the reverse transition. PREROLL DURATION {seconds}
- The number of seconds early an audio player is created, relative to start of crossfade (or end of song, if crossfade is 0). This affords a chance to open devices, establish connections and begin buffering streams. If set to 0, crossfade must also be disabled. Disabling preroll is necessary with somes media engines when an audio device can’t be opened more than once, to prevent skipping alternate songs due to device-busy errors.
Options available, exact meaning and behavior depends on the platform (i.e., Linux, Mac, etc.) and compile options. For more information on options, see libao drivers, libsdl outputs, libavdevice outputs, libavdevice protocols and gstreamer plug-ins.
Platform- & Library-Specific Audio Issues
gstreamer security
A pipeline may be specified for the gstreamer output, which has potential for security issues and abuse. When using gstreamer, it’s extra important to lock down security via means such as:
- Don’t run the daemon as root.
- Isolate the daemon in its own user account, without access to other users’ files.
- Prevent Internet access to pianod via a firewall.
- Keep visitor privilege level set low.
- Use good passwords on administrator accounts.
Linux
ffmpeg/libavdevice flush control
FFmpeg reports when audio has been fully flushed, but has proven unreliable with their built-in devices. Some devices report they are flushed, but closing them cuts off the audio (for example: ALSA); others indefinitely report they are still in the process of flushing (for example: Pulse).
To work around this, pianod adds two FFmpeg-style parameters to the OPTIONS string to control flushing:
- MIN_FLUSH_TIME
- The minimum amount of time, in milliseconds, allowed for flushing. Default is 0. If crossfading is disabled, and this value is too large, it will produce gaps between tracks. If crossfading, setting this value too large is innocuous.
- MAX_FLUSH_TIME
- The maximum time, in milliseconds, before pianod closes audio, even if it’s allegedly still buffering. Default is 2000, or 2 seconds.
Example:
room reconfigure options "min_flush_time=2000; max_flush_time=5000"
ffmpeg/libavdevice with ALSA
libavdevice may not default to the ALSA driver.
ROOM RECONFIGURE DRIVER "alsa"
Furthermore, some implementations/versions consider the ALSA driver experimental:
ROOM RECONFIGURE OPTIONS "strict=experimental"