Difference between revisions of "Bemix Notes"
m (→Stop) |
m (215 revisions imported) |
||
(93 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
− | [[Bemix]] is a web-based media player. It supports multiple soundcards and various networked client types, allowing a group to use a centralized server to control several speakers. Commands are issued through | + | [[Bemix]] is a web-based media player operating over standard HTTP. It supports multiple soundcards and various networked client types, allowing a group to use a centralized server to control several speakers. Commands are issued through URIs, where the resource portion of the address represents the command to be issued, while the query portion of the URI passes in parameters to the server. Multiple audio output devices are connected to a single computer (other configurations are also possible) defining an individual slave. Multiple slaves can join a single bemix server, allowing one server to manage audio playback on multiple devices on multiple computers. |
=Overview= | =Overview= | ||
− | + | The current implementation of the bemix system requires python2.3, mplayer, and an instance of MySQL. The server is designed to serve HTTP requests over port 9087. Any number of clients may gain access to the state of the bemix system and may issue commands to the server through query strings passed to the server in URIs. Communication between the server and clients is governed by the bemix client protocol. | |
− | The server acts as a central manager for any reasonable number of slaves that have joined with the server. The | + | The server acts as a central manager for any reasonable number of slaves that have joined with the server. The system supports multiple audio output devices on a single slave, and the server is capable of handling multiple slaves. Whenever a client wants to instruct a specific slave to perform a specific action, his request must be sent to the server, which then relays the request to the slave; clients never communicate directly with slaves. Communication between the server and the slaves are governed by the bemix slave protocol. |
=Philosophy and Design Goals= | =Philosophy and Design Goals= | ||
<p> | <p> | ||
− | The central philosphy of the bemix system is that music should be a group experience. To this end, the bemix system was designed to support a variety of music sources and control interfaces. Fundamental to this effort is a simple and reliable message delivery system. HTTP was chosen as the method of choice for sending messages due to its relative simplicity, established dependability, and | + | The central philosphy of the bemix system is that music should be a group experience. To this end, the bemix system was designed to support a variety of music sources and control interfaces. Fundamental to this effort is a simple and reliable message delivery system. HTTP was chosen as the method of choice for sending messages due to its relative simplicity, established dependability, and the prevalence of pre-existing HTTP libraries for a variety of environments. Communicating with any component in the bemix system is as simple as downloading or serving a webpage. |
</p> | </p> | ||
Line 16: | Line 16: | ||
<p> | <p> | ||
− | The client- | + | The client-server architecture is even more modularly designed, providing a variety of output formats for different types of clients. Currently html and plaintext 'client' output formats are planned, but as time permits an RDF output format is possible as well. Clients are designed to be either human or machine controlled. For instance, while a typical command-line client exists to allow users to manually send commands to the server, a computer-controlled alarm clock could easily be implemented which would instruct a specific client to play a specific playlist after a certain amount of time has elapsed. The server has no internal notion of an "alarm clock," but provided the bemix bot sends the appropriate URI to the server, the server will respond to the command with no knowledge of where it originates or how it is generated. The switches installed in the bemix bathroom are similarly controlled; a bemix bot listens for a signal from the touch switches and, once received, generates an appropriate URI and sends it to the sever. The bemix server itself has no notion of a "touch switch." |
</p> | </p> | ||
+ | |||
+ | =Obtaining Source= | ||
+ | To obtain the source code for the most recent version of bemix, it may be checked out anonymously from the subversion repository at svn://bemix.mit.edu/home/crowell/REPOS/bemix/trunk. To contribute back to the project, please send an email to the developers' list bemixdev at mit dot edu for instructions. | ||
=Bemix Server= | =Bemix Server= | ||
<p> | <p> | ||
− | Of all the components of the bemix system, the server is the least extensible or replaceable. This is likely not a drawback, however, as the bemix system is meant to allow a variety of slave and client types to communicate well with one another across a single server. For most situations, it is counter-productive for there to be multiple bemix servers to be active at the same time, | + | Of all the components of the bemix system, the server is the least extensible or replaceable. This is likely not a drawback, however, as the bemix system is meant to allow a variety of slave and client types to communicate well with one another across a single server. For most situations, it is counter-productive for there to be multiple bemix servers to be active at the same time, because the central design goal of bemix is to encourage a communal media experience though a single, common access point. |
</p> | </p> | ||
Line 27: | Line 30: | ||
* ''bemix/bemix-server.py'' - a small script that is run from the command line to begin the server processes | * ''bemix/bemix-server.py'' - a small script that is run from the command line to begin the server processes | ||
* ''bemix/serverlib.py'' - a comprehensive library containing various classes and support code for running the bemix server, communicating with slaves, and formatting output for clients | * ''bemix/serverlib.py'' - a comprehensive library containing various classes and support code for running the bemix server, communicating with slaves, and formatting output for clients | ||
+ | * ''bemix/playerlib.py'' - a library that contains interfaces for players and playlists, as well as the playlist implementation that is used by the server | ||
+ | * ''bemix/bemixlib.py'' - a library that contains several Exceptions that are used by the server, as well as methods to generate and parse errors, and to produce plaintext 'client' results | ||
+ | * ''crowell/browser.py'' - a library that deals with getting directory contents and creating relative and absolute file paths | ||
+ | * ''crowell/dictlib.py'' - a library that contains a method to determine whether or not all of the required elements in a URI are present | ||
==Configuration== | ==Configuration== | ||
Line 35: | Line 42: | ||
===Root Music Directory=== | ===Root Music Directory=== | ||
<p> | <p> | ||
− | The bemix server must have access to all the music that is available to any client on the bemix system. In other words, the server controls what music is and is not available to all the slaves connected to it. This decision ensures that a wide variety of music is available to all the slaves on the bemix system, and | + | The bemix server must have access to all the music that is available to any client on the bemix system. In other words, the server controls what music is and is not available to all the slaves connected to it. This decision ensures that a wide variety of music is available to all the slaves on the bemix system, and provides a 'barrier-free' music experience, allowing clients to queue up any song onto any player associated with any slave on the system without needing to know what files are available to the specific slave in question. Therefore, the music source must be available to both the server and all of its slaves through a network filesystem such as NFS, AFS, or CIFS. The server requires the files to live in or to be nested below a single directory on the filesystem, although this requirement can be met by providing symlinks to audio files and directories residing anywhere in the local filesystem hierarchy. Setting up the music shares is outside the scope of this document. |
</p> | </p> | ||
<p> | <p> | ||
− | It is important to note that the server does not actually transfer audio files to the slaves | + | It is important to note that the server does not actually transfer audio files to the slaves and is not capable of providing audio files for download to clients. Each slave must be configured in a manner similar to the server; the slaves must have access to the shared audio files in their local filesystem hierarchy as well. |
</p> | </p> | ||
Line 45: | Line 52: | ||
The bemix server utilizes a MySQL database to manage playlists, slaves, and other information about the configuration of the server. The following tables must be in place and accessible for the bemix-server to function properly. | The bemix server utilizes a MySQL database to manage playlists, slaves, and other information about the configuration of the server. The following tables must be in place and accessible for the bemix-server to function properly. | ||
− | + | ====Required Tables==== | |
− | + | ||
<pre> | <pre> | ||
+-----------------+ | +-----------------+ | ||
Line 57: | Line 63: | ||
+-----------------+ | +-----------------+ | ||
</pre> | </pre> | ||
− | |||
− | |||
<p> | <p> | ||
− | + | The MySQL user for the bemix database must have both read and write access to these four tables. | |
</p> | </p> | ||
+ | |||
+ | ====Data Table==== | ||
<pre> | <pre> | ||
+---------------+-------------+------+-----+---------+-------+ | +---------------+-------------+------+-----+---------+-------+ | ||
Line 70: | Line 76: | ||
+---------------+-------------+------+-----+---------+-------+ | +---------------+-------------+------+-----+---------+-------+ | ||
</pre> | </pre> | ||
− | |||
− | |||
<p> | <p> | ||
− | This table stores | + | This table stores various configuration variables that are required for the bemix server to function properly. Currently, the only defined variable is "rootDir" which has the value of the absolute path to the root music directory, such as "/mnt/tunes". |
</p> | </p> | ||
+ | |||
+ | ====Playlist Table==== | ||
<pre> | <pre> | ||
+------------+--------------+------+-----+---------+----------------+ | +------------+--------------+------+-----+---------+----------------+ | ||
Line 83: | Line 89: | ||
+------------+--------------+------+-----+---------+----------------+ | +------------+--------------+------+-----+---------+----------------+ | ||
</pre> | </pre> | ||
− | |||
− | |||
<p> | <p> | ||
− | This table | + | This table contains a record for each playlist that is created and automatically assigns each new playlist a unique id that is used as a foreign key the the track table, described below. <code>name</code> stores the name of the playlist (the value should be escaped as if it were part of a URI). When a playlist is deleted, its record in this table is removed as are any associated records in the track table. |
</p> | </p> | ||
+ | |||
+ | ====Track Table==== | ||
<pre> | <pre> | ||
+-------------+---------+------+-----+---------+----------------+ | +-------------+---------+------+-----+---------+----------------+ | ||
Line 98: | Line 104: | ||
+-------------+---------+------+-----+---------+----------------+ | +-------------+---------+------+-----+---------+----------------+ | ||
</pre> | </pre> | ||
− | |||
− | |||
<p> | <p> | ||
− | This table stores | + | This table stores the individual tracks that make up a playlist. While each track is individually assigned a unique <code>trackId</code>, this value is currently not utilized by the bemix server. <code>playlistId</code> refers to the playlist with the same id in the playlist table, described above. The index of the song in the playlist is stored in the field <code>trackNumber</code>, while the file path to the actual song is stored in the <code>file</code> field. (This file path is relative to the rootDir defined in the data table, and should be escaped as if it were being sent in a URI.) |
</p> | </p> | ||
+ | |||
+ | ====Slave Table==== | ||
<pre> | <pre> | ||
+---------+--------------+------+-----+---------+----------------+ | +---------+--------------+------+-----+---------+----------------+ | ||
Line 111: | Line 117: | ||
+---------+--------------+------+-----+---------+----------------+ | +---------+--------------+------+-----+---------+----------------+ | ||
</pre> | </pre> | ||
+ | <p> | ||
+ | This table stores information about the slaves that are allowed to join the bemix server. It does not, however, contain any information about which slaves happen to be connected to the server at any particular time. ''TODO:'' the <code>ip</code> field currently stores a string of the form 'http://1.2.3.4:9088/' rather than a pure IP address. | ||
+ | </p> | ||
+ | |||
+ | ==Output Formats== | ||
+ | The bemix server is capable of exporting data in several formats. The two currently supported formats are plaintext 'client' and html (suitable for viewing through a web browser). Clients are required to understand at least one of the available formats, but do not need to understand more than one. All operations will be available to clients that understand any of the available formats. | ||
+ | |||
+ | ===Plaintext=== | ||
+ | ''output=client'' | ||
+ | |||
+ | The plaintext 'client' format is a simple document consisting of a newline-delimited list of values. The order of the list is not guaranteed, but it can be assumed that all fields that are specified will be present in the document. All fields consist of a variable name represented as a string, followed by an '=' character, followed by the value of the variable. No spaces are present between the variable name, '=' character, and value, although space characters are permitted in the value (including a leading space character). Every value is terminated by a newline '\n' character, including the final line. If a list of values is to be returned, the value portion of the field is a comma-delimited string with no spaces inserted between individual values. There should be a comma at the end of the string as well, and the line should be terminated by a newline character. If a comma is needed as an individual value in a value string, it should be escaped as if it were to be included in a URI with the '%2C' escape sequence. | ||
+ | |||
+ | The only guaranteed field is the <code>success</code> field, which always takes on values of either 'true' or 'false'. A value of true indicates the operation was successfully carried out; a value of false indicates the operation could not be carried out for some reason. In the event of a failure, the <code>comment</code> and <code>error</code> my optionally be included to indicate the reason for the failure. If present, the value of <code>comment</code> is a human-readable string indicating the reason for the failure, steps to correct it, etc. If present, the <code>error</code> field is a machine-readable string indicating a reason for the failure. The list of legal error codes is interspersed within this document as part of the specification of each command. | ||
+ | |||
+ | ====Error Document==== | ||
+ | When an error is reported by the server, it comes in the following form: | ||
+ | <pre> | ||
+ | comment=the slaveId is not currently connected; it was last seen 3 days ago | ||
+ | error=invalid-slaveId | ||
+ | success=false | ||
+ | </pre> | ||
+ | |||
+ | ===HTML=== | ||
+ | ''output=html'' | ||
+ | |||
+ | This output format is suitable for viewing in a web browser. It utilizes cascading style sheets (CSS) and HTML forms to generate webpages that can be used to monitor and control the bemix system. Currently this output format is incomplete and so is not available for use. | ||
==Bemix Client Protocol== | ==Bemix Client Protocol== | ||
<p> | <p> | ||
− | The bemix client protocol is a one-way protocol that defines the form of messages sent to the server and the form of | + | The bemix client protocol is a one-way protocol that defines the form of messages sent to the server and the form of responses sent to the client. The server is never allowed to communicate with the client unless it is in response to a client request. The client sends a command to the server by sending a GET request for a specified resource, and the server sends its reply in the body of the document that is sent in response to the client. |
</p> | </p> | ||
<p> | <p> | ||
− | All commands are | + | All commands that are sent to the server by a bemix client are encoded in URIs with additional information contained in the query string. In the following documentation, all URIs are relative to the base server address (in our case http://bemix.mit.edu:9087/), and all URIs have an output=[html, client] element in their query string. An example URI that will instruct player 2 on slave 3 to begin playing the current playlist, and to report its status in the 'client' output format, is http://bemix.mit.edu:9087/play?output=client&slaveId=3&playerId=2. The commands that may be sent to the server include getting information about the slaves that are currently connected, creating a new playlist, and instructing a specific player to mute itself. |
</p> | </p> | ||
<p> | <p> | ||
− | In the following examples, a relative URI is listed indicating the form of the URI that is sent to the bemix server in order to accomplish a specific function. Listed after the URI are zero or more error codes along with a brief description of what can cause them. Following the error codes is sample bemix server output for a successful action in the plaintext 'client' output format. The query string indicates the type of the value of the variable as either <code>int</code> representing an integer or <code>string</code> representing an escaped sequence of utf-8 characters. | + | In the following examples, a relative URI is listed indicating the form of the URI that is sent to the bemix server in order to accomplish a specific function. Listed after the URI are zero or more error codes along with a brief description of what can cause them. Following the error codes is sample bemix server output for a successful action in the plaintext 'client' output format. The query string indicates the type of the value of the variable as either <code>int</code> representing an integer or <code>string</code> representing an escaped sequence of utf-8 characters. At the end of every command is a paragraph explaining in detail what the command does. |
</p> | </p> | ||
===Player=== | ===Player=== | ||
<p> | <p> | ||
− | A player represents a single | + | A player represents a single audio output device connected to a slave. There is one player for every audio output device that the bemix server knows about. Before anything can be played on a player, a playlist must be created, filled with songs, and loaded on the player. When a player has finished playing a song, it should automatically play the next song sequentially in the current playlist. If the song that just finished was the last song in the playlist, the player should begin playing the first song in the current playlist. A client never communicates directly with a slave or player; all commands for a specific player are sent via the bemix server which behaves much like a proxy, forwarding the request to the specified slave, and then forwarding the slave's response back to the client. |
</p> | </p> | ||
Line 133: | Line 165: | ||
''play?slaveId=int&playerId=int'' | ''play?slaveId=int&playerId=int'' | ||
* '''no-playlist-loaded''' - there is no playlist loaded for the current player | * '''no-playlist-loaded''' - there is no playlist loaded for the current player | ||
− | * ''' | + | * '''illegal-playlistId''' - the playlist that is currently loaded is no longer valid |
* '''invalid-slaveId''' - the slaveId that was specified is not valid | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
* '''invalid-playerId''' - the playerId that was specified is not valid | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
<pre> | <pre> | ||
+ | command=play | ||
success=true | success=true | ||
</pre> | </pre> | ||
<p> | <p> | ||
− | Instructs a player (referred to by its <code>playerId</code>) that is associated with a specific slave (referred to by its <code>slaveId</code>) to begin playing. If there is a playlist already loaded on the player, and the player is currently stopped, the player should begin playing from the beginning of the song that is stopped. If the player is paused, playback should be resumed. If the player is playing, the command should be ignored. The ''' | + | Instructs a player (referred to by its <code>playerId</code>) that is associated with a specific slave (referred to by its <code>slaveId</code>) to begin playing. If there is a playlist already loaded on the player, and the player is currently stopped, the player should begin playing from the beginning of the song that is stopped. If the player is paused, playback should be resumed. If the player is playing, the command should be ignored. The '''illegal-playlistId''' error arises if the playlist that is currently loaded on the player has subsequently been deleted and therefore cannot be played. |
</p> | </p> | ||
Line 146: | Line 179: | ||
''pause?slaveId=int&playerId=int'' | ''pause?slaveId=int&playerId=int'' | ||
* '''no-playlist-loaded''' - there is no playlist loaded for the current player | * '''no-playlist-loaded''' - there is no playlist loaded for the current player | ||
− | * ''' | + | * '''illegal-playlistId''' - the playlist that is currently loaded is no longer valid |
* '''invalid-slaveId''' - the slaveId that was specified is not valid | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
* '''invalid-playerId''' - the playerId that was specified is not valid | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
<pre> | <pre> | ||
+ | command=pause | ||
success=true | success=true | ||
</pre> | </pre> | ||
Line 159: | Line 193: | ||
''stop?slaveId=int&playerId=x'' | ''stop?slaveId=int&playerId=x'' | ||
* '''no-playlist-loaded''' - there is no playlist loaded for the current player | * '''no-playlist-loaded''' - there is no playlist loaded for the current player | ||
− | * ''' | + | * '''illegal-playlistId''' - the playlist that is currently loaded is no longer valid |
* '''invalid-slaveId''' - the slaveId that was specified is not valid | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
* '''invalid-playerId''' - the playerId that was specified is not valid | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
<pre> | <pre> | ||
+ | command=stop | ||
success=true | success=true | ||
</pre> | </pre> | ||
<p> | <p> | ||
Instructs a player to stop playing. If the player is currently playing or paused then playback stops and the position in the current song is reset to 0 so that if it is later played, playback begins from the beginning of the song. If the player is currently stopped then the command is ignored. | Instructs a player to stop playing. If the player is currently playing or paused then playback stops and the position in the current song is reset to 0 so that if it is later played, playback begins from the beginning of the song. If the player is currently stopped then the command is ignored. | ||
+ | </p> | ||
+ | |||
+ | ====Toggle==== | ||
+ | ''toggleStatus?slaveId=int&playerId=int'' | ||
+ | * '''no-playlist-loaded''' - there is no playlist loaded for the current player | ||
+ | * '''illegal-playlistId''' - the playlist that is currently loaded is no longer valid | ||
+ | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=toggleStatus | ||
+ | success=true | ||
+ | </pre> | ||
+ | <p> | ||
+ | Instructs a player to toggle its current status. If the player is playing, playback will stop. If the player is stopped, it will begin playing. If the player is paused, it will be resumed. | ||
+ | </p> | ||
+ | |||
+ | ====Next==== | ||
+ | ''next?slaveId=int&playerId=int'' | ||
+ | * '''no-playlist-loaded''' - there is no playlist loaded for the current player | ||
+ | * '''illegal-playlistId''' - the playlist that is currently loaded is no longer valid | ||
+ | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=next | ||
+ | success=true | ||
+ | </pre> | ||
+ | <p> | ||
+ | Instructs a player to begin playing the next song. As long as a playlist is loaded, the next song in the playlist begins to play immediately. If the current song is the last song in the playlist, the first song in the playlist begins playing. | ||
+ | </p> | ||
+ | |||
+ | ====Previous==== | ||
+ | ''previous?slaveId=int&playerId=int'' | ||
+ | * '''no-playlist-loaded''' - there is no playlist loaded for the current player | ||
+ | * '''illegal-playlistId''' - the playlist that is currently loaded is no longer valid | ||
+ | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=previous | ||
+ | success=true | ||
+ | </pre> | ||
+ | <p> | ||
+ | Instructs a player to begin playing the previous song. As long as a playlist is loaded, the previous song in the playlist begins to play immediately. If the current song is the first song in the playlist, the last song in the playlist begins playing. | ||
</p> | </p> | ||
====Load==== | ====Load==== | ||
''load?slaveId=int&playerId=int&playlistId=int'' | ''load?slaveId=int&playerId=int&playlistId=int'' | ||
− | |||
* '''invalid-playlistId''' - the playlist that was specified is not valid | * '''invalid-playlistId''' - the playlist that was specified is not valid | ||
* '''invalid-slaveId''' - the slaveId that was specified is not valid | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
* '''invalid-playerId''' - the playerId that was specified is not valid | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
<pre> | <pre> | ||
+ | command=load | ||
success=true | success=true | ||
</pre> | </pre> | ||
<p> | <p> | ||
− | Instructs a player to load a specified playlist. Regardless of the state of the player, playback stops | + | Instructs a player to load a specified playlist. Regardless of the state of the player, playback stops, the playlist is loaded, and the first song in the playlist begins playing immediately. A 'play' command does not have to be sent in order for the player to begin playing when a playlist is loaded. |
</p> | </p> | ||
+ | ====Unload==== | ||
+ | ''unload?slaveId=int&playerId=int'' | ||
+ | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=unload | ||
+ | success=true | ||
+ | </pre> | ||
+ | <p> | ||
+ | Instructs a player to unload a playlist if one is currently loaded. This returns the player to the state it is in after it first joins the server. If the player is currently unloaded then the command is ignored. Playback immediately stops for the current player. | ||
+ | </p> | ||
− | + | ====Seek==== | |
− | + | ''seek?slaveId=int&playerId=int&position=int'' | |
− | + | * '''no-playlist-loaded''' - there is no playlist loaded for the current player | |
− | * | + | * '''illegal-playlistId''' - the playlist that is currently loaded is no longer valid |
− | + | * '''invalid-slaveId''' - the slaveId that was specified is not valid | |
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=seek | ||
+ | success=true | ||
+ | </pre> | ||
+ | |||
+ | Instructs a player to play the current song at the indicated position (represented as seconds). As long as a playlist is loaded, the player immediately begins playing the current song at the indiciated position. If the position is less than 0, playback begins at the start of the song. If the position is larger than the length of the song the command is equivalent to a 'next' command. | ||
+ | |||
+ | ====Set Volume==== | ||
+ | ''setVolume?slaveId=int&playerId=int&level=int'' | ||
+ | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=setVolume | ||
+ | success=true | ||
+ | </pre> | ||
+ | |||
+ | Instructs a player to set its internal volume to a specified level. The level should be between 0 and 100. If level is below 0, it should be set to 0. If level is larger than 100, it should be set to 100. | ||
+ | |||
+ | ====Adjust Volume==== | ||
+ | ''adjustVolume?slaveId=int&playerId=int&amount=int'' | ||
+ | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=adjustVolume | ||
+ | success=true | ||
+ | </pre> | ||
+ | Instructs a player to adjust its internal volume by a specified amount. The amount should be between 0 and 100. If amount is below 0, it should be set to 0. If amount is larger than 100, it should be set to 100. The amound may be negative to instruct the player to reduce the current volume by a certain amount. | ||
+ | |||
+ | ====Status==== | ||
+ | ''player?slaveId=int&slaveId=int'' | ||
+ | * '''invalid-slaveId''' - the slaveId that was specified is not valid | ||
+ | * '''invalid-playerId''' - the playerId that was specified is not valid | ||
+ | <pre> | ||
+ | command=player | ||
+ | slaveId=int | ||
+ | playerId=int | ||
+ | name=string | ||
+ | playlistId=int | ||
+ | index=int | ||
+ | volume=int | ||
+ | secondsElapsed=float | ||
+ | secondsTotal=float | ||
+ | status=int | ||
+ | success=true | ||
+ | </pre> | ||
+ | <p> | ||
+ | Gets the current status of the specified player. <code>name</code> represents the name of the player. <code>playlistId</code> represents the id of the currently loaded playlist, or -1 if there is no playlist loaded. <code>index</code> represents the index number of the currently selected track, or -1 if there is no playlist loaded. <code>volume</code> indicates the current volume level from 0-100. <code>secondsElapsed</code> represents the current position in the current song in seconds. <code>secondsTotal</code> represents the length of the current song in seconds. <code>status</code> represents the status of the player; -1 indicates no playlist loaded, 0 indicates playing, 1 indicates paused, 2 indicates stopped. | ||
+ | </p> | ||
===Playlist=== | ===Playlist=== | ||
− | A playlist consists of a name and music files that are associated with it. The songs in a playlist are ordered with indices starting at 0. Before anything can be played, a playlist must be created and loaded onto a specific player. | + | A playlist consists of a name and music files that are associated with it. The songs in a playlist are ordered with indices starting at 0. Before anything can be played, a playlist must be created and loaded onto a specific player. When a playlist is created it is assigned a unique and new id by the server which acts as the playlist's identifier for its entire life. When a playlist is deleted its id is not recycled, and can never be used again. |
− | + | ====Available Playlists==== | |
− | + | ''playlists'' | |
− | + | <pre> | |
− | * remove?playlistId= | + | command=playlists |
− | + | playlistIds=int,int,int,...,int, | |
+ | success=true | ||
+ | </pre> | ||
+ | Gets a list of all the valid <code>playlistIds</code>. This list does not include playlists that have been deleted. | ||
+ | |||
+ | ====Create==== | ||
+ | ''create?name=string'' | ||
+ | <pre> | ||
+ | command=create | ||
+ | name=string | ||
+ | playlistId=int | ||
+ | success=true | ||
+ | </pre> | ||
+ | Instructs the server to create a new playlist and returns the playlistId of the newly created playlist as <code>playlistId</code>. The newly generated playlistId must be unique and new (no other playlists have the same playlistId, and no playlists may have ever had the same playlistId, even those that have been deleted). <code>name</code> is the name of the playlist that was just created. | ||
+ | |||
+ | ====Delete==== | ||
+ | ''delete?playlistId=int'' | ||
+ | * '''invalid-playlistId''' - the playlist that was specified is not valid | ||
+ | <pre> | ||
+ | command=delete | ||
+ | success=true | ||
+ | </pre> | ||
+ | Instructs the server to remove a specified playlist from the list. Once the playlist has been removed it cannot be restored. | ||
+ | |||
+ | ====Add Track==== | ||
+ | ''add?playlistId=int&song=string'' | ||
+ | * '''invalid-playlistId''' - the playlist that was specified is not valid | ||
+ | * '''invalid-song''' - the song is not valid | ||
+ | <pre> | ||
+ | command=add | ||
+ | index=int | ||
+ | song=string | ||
+ | playlistId=int | ||
+ | success=true | ||
+ | </pre> | ||
+ | Adds a specified song to the end of the indicated playlist. The song name should be relative to the rootDir defined by the server; i.e. the client should indicate the song relative to '/'. <code>index</code> indicates the position of the newly added song in the playlist. <code>song</code> is a string giving the relative path of the song that was added. | ||
+ | |||
+ | ====Add Directory==== | ||
+ | ''addDir?playlistId=int&dir=string'' | ||
+ | * '''invalid-playlistid''' - the playlist that was specified is not valid | ||
+ | * '''invalid-song''' - the song is not valid | ||
+ | <pre> | ||
+ | command=addDir | ||
+ | dir=string | ||
+ | playlistId=int | ||
+ | success=true | ||
+ | </pre> | ||
+ | Adds a specified directory to a playlist. Result is the same as using ''add'' once for each file in a directory. | ||
+ | |||
+ | ====Remove Track==== | ||
+ | ''remove?playlistId=int&index=int'' | ||
+ | * '''invalid-playlistId''' - the playlist that was specified is not valid | ||
+ | * '''invalid-index''' - the index specified is not valid for the playlist | ||
+ | <pre> | ||
+ | command=remove | ||
+ | song=string | ||
+ | success=true | ||
+ | </pre> | ||
+ | Removes a specified song from a playlist. The song is referred to by its index in the playlist rather than by its name. <code>song</code> is the relative path of the song that was removed from the playlist. | ||
+ | |||
+ | ====Playlist Contents==== | ||
+ | ''playlist?playlistId=int'' | ||
+ | * '''invalid-playlistId''' - the playlist that was specified is not valid | ||
+ | <pre> | ||
+ | command=playlist | ||
+ | playlistId=int | ||
+ | name=string | ||
+ | size=int | ||
+ | song0=string | ||
+ | song1=string | ||
+ | ... | ||
+ | success=true | ||
+ | </pre> | ||
+ | Gets the status of a specified playlist. <code>name</code> indicates the name of the playlist. <code>size</code> indicates the number of songs in the playlist. <code>songN</code> indicates the relative path of the song in the playlist. If the size of the playlist is N, there should be values for <code>song0</code>, <code>song1</code>, ... <code>song(N-1)</code>. | ||
===Browsing=== | ===Browsing=== | ||
− | + | The server contains a list of all the music files available to any slave connected to it. Therefore the server can handle any requests for file browsing regardless of the slaves that are connected to it at any given time. All operations on files and directories are relative to the server's root music directory, so ''/A/Air'' may actually refer to ''/mnt/tunes/A/Air'' on the server. | |
− | + | ====Directory Contents==== | |
+ | ''browse?dir=string'' | ||
+ | * '''invalid-directory''' - the directory is not accessible by the server | ||
+ | <pre> | ||
+ | command=browse | ||
+ | dir=string | ||
+ | dir0=string | ||
+ | dir1=string | ||
+ | ... | ||
+ | file0=string | ||
+ | file1=string | ||
+ | ... | ||
+ | success=true | ||
+ | </pre> | ||
+ | Instructs the server to list the contents of the directory specified by dir. This directory should be specified relative to the server's root music directory. <code>dirN</code> represents the complete relative path of a subdirectory of the directory. <code>fileN</code> represents the complete relative path of a file contained in the directory. <code>dir</code> (with no number afterwards) represents the directory whose contents are being displayed. | ||
− | === | + | ===Slaves=== |
− | + | A single bemix server can control multiple slaves that are connected to it. A slave is assigned a unique id when it registers with the server, and this id is used to represent the slave forever. A slave is guaranteed to have the same id whenever it is connected, even across sessions. | |
− | + | ====Available Slaves==== | |
− | * | + | ''slaves'' |
+ | <pre> | ||
+ | command=slaves | ||
+ | slaveIds=int,int,int,...,int, | ||
+ | success=true | ||
+ | </pre> | ||
+ | Gets a list of the <code>slaveIds</code> for all slaves that are currently connected to the server. | ||
+ | |||
+ | ====Slave Information==== | ||
+ | ''slave?slaveId=int'' | ||
+ | * '''invalid-slaveId''' - the slaveId is not valid | ||
+ | <pre> | ||
+ | command=slave | ||
+ | slaveId=int | ||
+ | name=string | ||
+ | playerIds=int,int,int,...int, | ||
+ | success=true | ||
+ | </pre> | ||
+ | Gets information about the specified slave. <code>playerIds</code> indicates a list of all the valid players that are connected to this slave. Once the slave has joined, the list of playerIds may not be modified unless the slave disconnects and then rejoins the server. | ||
=Results= | =Results= |
Latest revision as of 22:53, 25 August 2015
Bemix is a web-based media player operating over standard HTTP. It supports multiple soundcards and various networked client types, allowing a group to use a centralized server to control several speakers. Commands are issued through URIs, where the resource portion of the address represents the command to be issued, while the query portion of the URI passes in parameters to the server. Multiple audio output devices are connected to a single computer (other configurations are also possible) defining an individual slave. Multiple slaves can join a single bemix server, allowing one server to manage audio playback on multiple devices on multiple computers.
Contents
Overview
The current implementation of the bemix system requires python2.3, mplayer, and an instance of MySQL. The server is designed to serve HTTP requests over port 9087. Any number of clients may gain access to the state of the bemix system and may issue commands to the server through query strings passed to the server in URIs. Communication between the server and clients is governed by the bemix client protocol.
The server acts as a central manager for any reasonable number of slaves that have joined with the server. The system supports multiple audio output devices on a single slave, and the server is capable of handling multiple slaves. Whenever a client wants to instruct a specific slave to perform a specific action, his request must be sent to the server, which then relays the request to the slave; clients never communicate directly with slaves. Communication between the server and the slaves are governed by the bemix slave protocol.
Philosophy and Design Goals
The central philosphy of the bemix system is that music should be a group experience. To this end, the bemix system was designed to support a variety of music sources and control interfaces. Fundamental to this effort is a simple and reliable message delivery system. HTTP was chosen as the method of choice for sending messages due to its relative simplicity, established dependability, and the prevalence of pre-existing HTTP libraries for a variety of environments. Communicating with any component in the bemix system is as simple as downloading or serving a webpage.
The server-slave architecture enables numerous remote machines to register themselves with a single server, thereby providing a single common interface for access to a wide variety of audio sources. Due to the simple nature of the server-slave architecture, a variety of slaves may be implemented. The base implementation utilizes mplayer for audio playback, but other slaves using xmms, winamp, or other audio players is possible as well. The only requirement for a slave is that it fully implement the bemix slave protocol.
The client-server architecture is even more modularly designed, providing a variety of output formats for different types of clients. Currently html and plaintext 'client' output formats are planned, but as time permits an RDF output format is possible as well. Clients are designed to be either human or machine controlled. For instance, while a typical command-line client exists to allow users to manually send commands to the server, a computer-controlled alarm clock could easily be implemented which would instruct a specific client to play a specific playlist after a certain amount of time has elapsed. The server has no internal notion of an "alarm clock," but provided the bemix bot sends the appropriate URI to the server, the server will respond to the command with no knowledge of where it originates or how it is generated. The switches installed in the bemix bathroom are similarly controlled; a bemix bot listens for a signal from the touch switches and, once received, generates an appropriate URI and sends it to the sever. The bemix server itself has no notion of a "touch switch."
Obtaining Source
To obtain the source code for the most recent version of bemix, it may be checked out anonymously from the subversion repository at svn://bemix.mit.edu/home/crowell/REPOS/bemix/trunk. To contribute back to the project, please send an email to the developers' list bemixdev at mit dot edu for instructions.
Bemix Server
Of all the components of the bemix system, the server is the least extensible or replaceable. This is likely not a drawback, however, as the bemix system is meant to allow a variety of slave and client types to communicate well with one another across a single server. For most situations, it is counter-productive for there to be multiple bemix servers to be active at the same time, because the central design goal of bemix is to encourage a communal media experience though a single, common access point.
Source files of interest:
- bemix/bemix-server.py - a small script that is run from the command line to begin the server processes
- bemix/serverlib.py - a comprehensive library containing various classes and support code for running the bemix server, communicating with slaves, and formatting output for clients
- bemix/playerlib.py - a library that contains interfaces for players and playlists, as well as the playlist implementation that is used by the server
- bemix/bemixlib.py - a library that contains several Exceptions that are used by the server, as well as methods to generate and parse errors, and to produce plaintext 'client' results
- crowell/browser.py - a library that deals with getting directory contents and creating relative and absolute file paths
- crowell/dictlib.py - a library that contains a method to determine whether or not all of the required elements in a URI are present
Configuration
The bemix server needs an open port to listen on; by default this port is 9087. It also requires read and write access to a database named 'bemix' where it stores and loads various configuration parameters. In the future an external configuration file and/or a suite of configuration utilities are planned; currently all configuration must be done manually (and at times through direct manipulation of the source code).
Root Music Directory
The bemix server must have access to all the music that is available to any client on the bemix system. In other words, the server controls what music is and is not available to all the slaves connected to it. This decision ensures that a wide variety of music is available to all the slaves on the bemix system, and provides a 'barrier-free' music experience, allowing clients to queue up any song onto any player associated with any slave on the system without needing to know what files are available to the specific slave in question. Therefore, the music source must be available to both the server and all of its slaves through a network filesystem such as NFS, AFS, or CIFS. The server requires the files to live in or to be nested below a single directory on the filesystem, although this requirement can be met by providing symlinks to audio files and directories residing anywhere in the local filesystem hierarchy. Setting up the music shares is outside the scope of this document.
It is important to note that the server does not actually transfer audio files to the slaves and is not capable of providing audio files for download to clients. Each slave must be configured in a manner similar to the server; the slaves must have access to the shared audio files in their local filesystem hierarchy as well.
MySQL Table Schema
The bemix server utilizes a MySQL database to manage playlists, slaves, and other information about the configuration of the server. The following tables must be in place and accessible for the bemix-server to function properly.
Required Tables
+-----------------+ | Tables_in_bemix | +-----------------+ | data | | playlist | | slave | | track | +-----------------+
The MySQL user for the bemix database must have both read and write access to these four tables.
Data Table
+---------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+-------------+------+-----+---------+-------+ | variable | varchar(50) | | PRI | | | | variableValue | varchar(50) | YES | | NULL | | +---------------+-------------+------+-----+---------+-------+
This table stores various configuration variables that are required for the bemix server to function properly. Currently, the only defined variable is "rootDir" which has the value of the absolute path to the root music directory, such as "/mnt/tunes".
Playlist Table
+------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | playlistId | int(11) | | PRI | NULL | auto_increment | | name | varchar(100) | | | | | +------------+--------------+------+-----+---------+----------------+
This table contains a record for each playlist that is created and automatically assigns each new playlist a unique id that is used as a foreign key the the track table, described below. name
stores the name of the playlist (the value should be escaped as if it were part of a URI). When a playlist is deleted, its record in this table is removed as are any associated records in the track table.
Track Table
+-------------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+---------+------+-----+---------+----------------+ | trackId | int(11) | | PRI | NULL | auto_increment | | playlistId | int(11) | | | 0 | | | trackNumber | int(11) | | | 0 | | | file | text | YES | | NULL | | +-------------+---------+------+-----+---------+----------------+
This table stores the individual tracks that make up a playlist. While each track is individually assigned a unique trackId
, this value is currently not utilized by the bemix server. playlistId
refers to the playlist with the same id in the playlist table, described above. The index of the song in the playlist is stored in the field trackNumber
, while the file path to the actual song is stored in the file
field. (This file path is relative to the rootDir defined in the data table, and should be escaped as if it were being sent in a URI.)
Slave Table
+---------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+----------------+ | slaveId | int(11) | | PRI | NULL | auto_increment | | ip | varchar(100) | YES | | NULL | | +---------+--------------+------+-----+---------+----------------+
This table stores information about the slaves that are allowed to join the bemix server. It does not, however, contain any information about which slaves happen to be connected to the server at any particular time. TODO: the ip
field currently stores a string of the form 'http://1.2.3.4:9088/' rather than a pure IP address.
Output Formats
The bemix server is capable of exporting data in several formats. The two currently supported formats are plaintext 'client' and html (suitable for viewing through a web browser). Clients are required to understand at least one of the available formats, but do not need to understand more than one. All operations will be available to clients that understand any of the available formats.
Plaintext
output=client
The plaintext 'client' format is a simple document consisting of a newline-delimited list of values. The order of the list is not guaranteed, but it can be assumed that all fields that are specified will be present in the document. All fields consist of a variable name represented as a string, followed by an '=' character, followed by the value of the variable. No spaces are present between the variable name, '=' character, and value, although space characters are permitted in the value (including a leading space character). Every value is terminated by a newline '\n' character, including the final line. If a list of values is to be returned, the value portion of the field is a comma-delimited string with no spaces inserted between individual values. There should be a comma at the end of the string as well, and the line should be terminated by a newline character. If a comma is needed as an individual value in a value string, it should be escaped as if it were to be included in a URI with the '%2C' escape sequence.
The only guaranteed field is the success
field, which always takes on values of either 'true' or 'false'. A value of true indicates the operation was successfully carried out; a value of false indicates the operation could not be carried out for some reason. In the event of a failure, the comment
and error
my optionally be included to indicate the reason for the failure. If present, the value of comment
is a human-readable string indicating the reason for the failure, steps to correct it, etc. If present, the error
field is a machine-readable string indicating a reason for the failure. The list of legal error codes is interspersed within this document as part of the specification of each command.
Error Document
When an error is reported by the server, it comes in the following form:
comment=the slaveId is not currently connected; it was last seen 3 days ago error=invalid-slaveId success=false
HTML
output=html
This output format is suitable for viewing in a web browser. It utilizes cascading style sheets (CSS) and HTML forms to generate webpages that can be used to monitor and control the bemix system. Currently this output format is incomplete and so is not available for use.
Bemix Client Protocol
The bemix client protocol is a one-way protocol that defines the form of messages sent to the server and the form of responses sent to the client. The server is never allowed to communicate with the client unless it is in response to a client request. The client sends a command to the server by sending a GET request for a specified resource, and the server sends its reply in the body of the document that is sent in response to the client.
All commands that are sent to the server by a bemix client are encoded in URIs with additional information contained in the query string. In the following documentation, all URIs are relative to the base server address (in our case http://bemix.mit.edu:9087/), and all URIs have an output=[html, client] element in their query string. An example URI that will instruct player 2 on slave 3 to begin playing the current playlist, and to report its status in the 'client' output format, is http://bemix.mit.edu:9087/play?output=client&slaveId=3&playerId=2. The commands that may be sent to the server include getting information about the slaves that are currently connected, creating a new playlist, and instructing a specific player to mute itself.
In the following examples, a relative URI is listed indicating the form of the URI that is sent to the bemix server in order to accomplish a specific function. Listed after the URI are zero or more error codes along with a brief description of what can cause them. Following the error codes is sample bemix server output for a successful action in the plaintext 'client' output format. The query string indicates the type of the value of the variable as either int
representing an integer or string
representing an escaped sequence of utf-8 characters. At the end of every command is a paragraph explaining in detail what the command does.
Player
A player represents a single audio output device connected to a slave. There is one player for every audio output device that the bemix server knows about. Before anything can be played on a player, a playlist must be created, filled with songs, and loaded on the player. When a player has finished playing a song, it should automatically play the next song sequentially in the current playlist. If the song that just finished was the last song in the playlist, the player should begin playing the first song in the current playlist. A client never communicates directly with a slave or player; all commands for a specific player are sent via the bemix server which behaves much like a proxy, forwarding the request to the specified slave, and then forwarding the slave's response back to the client.
Play
play?slaveId=int&playerId=int
- no-playlist-loaded - there is no playlist loaded for the current player
- illegal-playlistId - the playlist that is currently loaded is no longer valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=play success=true
Instructs a player (referred to by its playerId
) that is associated with a specific slave (referred to by its slaveId
) to begin playing. If there is a playlist already loaded on the player, and the player is currently stopped, the player should begin playing from the beginning of the song that is stopped. If the player is paused, playback should be resumed. If the player is playing, the command should be ignored. The illegal-playlistId error arises if the playlist that is currently loaded on the player has subsequently been deleted and therefore cannot be played.
Pause
pause?slaveId=int&playerId=int
- no-playlist-loaded - there is no playlist loaded for the current player
- illegal-playlistId - the playlist that is currently loaded is no longer valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=pause success=true
Instructs a player to pause or resume the current song. If the player is currently playing then the song is paused. If the player is currently paused then the song is resumed ans begins playing from the position at which it was previously paused. If the player is stopped the command is ignored.
Stop
stop?slaveId=int&playerId=x
- no-playlist-loaded - there is no playlist loaded for the current player
- illegal-playlistId - the playlist that is currently loaded is no longer valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=stop success=true
Instructs a player to stop playing. If the player is currently playing or paused then playback stops and the position in the current song is reset to 0 so that if it is later played, playback begins from the beginning of the song. If the player is currently stopped then the command is ignored.
Toggle
toggleStatus?slaveId=int&playerId=int
- no-playlist-loaded - there is no playlist loaded for the current player
- illegal-playlistId - the playlist that is currently loaded is no longer valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=toggleStatus success=true
Instructs a player to toggle its current status. If the player is playing, playback will stop. If the player is stopped, it will begin playing. If the player is paused, it will be resumed.
Next
next?slaveId=int&playerId=int
- no-playlist-loaded - there is no playlist loaded for the current player
- illegal-playlistId - the playlist that is currently loaded is no longer valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=next success=true
Instructs a player to begin playing the next song. As long as a playlist is loaded, the next song in the playlist begins to play immediately. If the current song is the last song in the playlist, the first song in the playlist begins playing.
Previous
previous?slaveId=int&playerId=int
- no-playlist-loaded - there is no playlist loaded for the current player
- illegal-playlistId - the playlist that is currently loaded is no longer valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=previous success=true
Instructs a player to begin playing the previous song. As long as a playlist is loaded, the previous song in the playlist begins to play immediately. If the current song is the first song in the playlist, the last song in the playlist begins playing.
Load
load?slaveId=int&playerId=int&playlistId=int
- invalid-playlistId - the playlist that was specified is not valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=load success=true
Instructs a player to load a specified playlist. Regardless of the state of the player, playback stops, the playlist is loaded, and the first song in the playlist begins playing immediately. A 'play' command does not have to be sent in order for the player to begin playing when a playlist is loaded.
Unload
unload?slaveId=int&playerId=int
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=unload success=true
Instructs a player to unload a playlist if one is currently loaded. This returns the player to the state it is in after it first joins the server. If the player is currently unloaded then the command is ignored. Playback immediately stops for the current player.
Seek
seek?slaveId=int&playerId=int&position=int
- no-playlist-loaded - there is no playlist loaded for the current player
- illegal-playlistId - the playlist that is currently loaded is no longer valid
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=seek success=true
Instructs a player to play the current song at the indicated position (represented as seconds). As long as a playlist is loaded, the player immediately begins playing the current song at the indiciated position. If the position is less than 0, playback begins at the start of the song. If the position is larger than the length of the song the command is equivalent to a 'next' command.
Set Volume
setVolume?slaveId=int&playerId=int&level=int
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=setVolume success=true
Instructs a player to set its internal volume to a specified level. The level should be between 0 and 100. If level is below 0, it should be set to 0. If level is larger than 100, it should be set to 100.
Adjust Volume
adjustVolume?slaveId=int&playerId=int&amount=int
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=adjustVolume success=true
Instructs a player to adjust its internal volume by a specified amount. The amount should be between 0 and 100. If amount is below 0, it should be set to 0. If amount is larger than 100, it should be set to 100. The amound may be negative to instruct the player to reduce the current volume by a certain amount.
Status
player?slaveId=int&slaveId=int
- invalid-slaveId - the slaveId that was specified is not valid
- invalid-playerId - the playerId that was specified is not valid
command=player slaveId=int playerId=int name=string playlistId=int index=int volume=int secondsElapsed=float secondsTotal=float status=int success=true
Gets the current status of the specified player. name
represents the name of the player. playlistId
represents the id of the currently loaded playlist, or -1 if there is no playlist loaded. index
represents the index number of the currently selected track, or -1 if there is no playlist loaded. volume
indicates the current volume level from 0-100. secondsElapsed
represents the current position in the current song in seconds. secondsTotal
represents the length of the current song in seconds. status
represents the status of the player; -1 indicates no playlist loaded, 0 indicates playing, 1 indicates paused, 2 indicates stopped.
Playlist
A playlist consists of a name and music files that are associated with it. The songs in a playlist are ordered with indices starting at 0. Before anything can be played, a playlist must be created and loaded onto a specific player. When a playlist is created it is assigned a unique and new id by the server which acts as the playlist's identifier for its entire life. When a playlist is deleted its id is not recycled, and can never be used again.
Available Playlists
playlists
command=playlists playlistIds=int,int,int,...,int, success=true
Gets a list of all the valid playlistIds
. This list does not include playlists that have been deleted.
Create
create?name=string
command=create name=string playlistId=int success=true
Instructs the server to create a new playlist and returns the playlistId of the newly created playlist as playlistId
. The newly generated playlistId must be unique and new (no other playlists have the same playlistId, and no playlists may have ever had the same playlistId, even those that have been deleted). name
is the name of the playlist that was just created.
Delete
delete?playlistId=int
- invalid-playlistId - the playlist that was specified is not valid
command=delete success=true
Instructs the server to remove a specified playlist from the list. Once the playlist has been removed it cannot be restored.
Add Track
add?playlistId=int&song=string
- invalid-playlistId - the playlist that was specified is not valid
- invalid-song - the song is not valid
command=add index=int song=string playlistId=int success=true
Adds a specified song to the end of the indicated playlist. The song name should be relative to the rootDir defined by the server; i.e. the client should indicate the song relative to '/'. index
indicates the position of the newly added song in the playlist. song
is a string giving the relative path of the song that was added.
Add Directory
addDir?playlistId=int&dir=string
- invalid-playlistid - the playlist that was specified is not valid
- invalid-song - the song is not valid
command=addDir dir=string playlistId=int success=true
Adds a specified directory to a playlist. Result is the same as using add once for each file in a directory.
Remove Track
remove?playlistId=int&index=int
- invalid-playlistId - the playlist that was specified is not valid
- invalid-index - the index specified is not valid for the playlist
command=remove song=string success=true
Removes a specified song from a playlist. The song is referred to by its index in the playlist rather than by its name. song
is the relative path of the song that was removed from the playlist.
Playlist Contents
playlist?playlistId=int
- invalid-playlistId - the playlist that was specified is not valid
command=playlist playlistId=int name=string size=int song0=string song1=string ... success=true
Gets the status of a specified playlist. name
indicates the name of the playlist. size
indicates the number of songs in the playlist. songN
indicates the relative path of the song in the playlist. If the size of the playlist is N, there should be values for song0
, song1
, ... song(N-1)
.
Browsing
The server contains a list of all the music files available to any slave connected to it. Therefore the server can handle any requests for file browsing regardless of the slaves that are connected to it at any given time. All operations on files and directories are relative to the server's root music directory, so /A/Air may actually refer to /mnt/tunes/A/Air on the server.
Directory Contents
browse?dir=string
- invalid-directory - the directory is not accessible by the server
command=browse dir=string dir0=string dir1=string ... file0=string file1=string ... success=true
Instructs the server to list the contents of the directory specified by dir. This directory should be specified relative to the server's root music directory. dirN
represents the complete relative path of a subdirectory of the directory. fileN
represents the complete relative path of a file contained in the directory. dir
(with no number afterwards) represents the directory whose contents are being displayed.
Slaves
A single bemix server can control multiple slaves that are connected to it. A slave is assigned a unique id when it registers with the server, and this id is used to represent the slave forever. A slave is guaranteed to have the same id whenever it is connected, even across sessions.
Available Slaves
slaves
command=slaves slaveIds=int,int,int,...,int, success=true
Gets a list of the slaveIds
for all slaves that are currently connected to the server.
Slave Information
slave?slaveId=int
- invalid-slaveId - the slaveId is not valid
command=slave slaveId=int name=string playerIds=int,int,int,...int, success=true
Gets information about the specified slave. playerIds
indicates a list of all the valid players that are connected to this slave. Once the slave has joined, the list of playerIds may not be modified unless the slave disconnects and then rejoins the server.
Results
Each command returns a data structure through HTTP that defines variable names and values. A (variable name, value) combination is referred to as a field. Every command contains at least one success
field, whose value may be either true
or false
. If any tag is false
, it should be assumed that the entire command failed. Each command may optionally return a comment
field as well, whose value gives some indication of why an operation may have failed.
In addition, most commands return additional information about the state of a particular aspect of the bemix server.
Slave Commands
Slave commands are, in general, identical in form to their "server" counterparts defined above. However, there are additional commands which are defined for the bemix server to communicate with its slaves. The slave server understands all commands understood by the server that are listed under "Player", as well as the "players" command.
Additional Server Commands
The bemix server understands additional commands that are meant to be used only by bemix slaves. Clients should not use these commands themselves.
- playlistSong?playlistId=x&index=y (gets the name of the song at a specified index in a given playlist)
Client Types
Bemix is capable of dumping data in multiple of formats. Currently only the "client" and "html" formats are planned, but XML/RDF formats are possibility in the future. The format of the export is indicated in the URL by the mandatory output
parameter.
Client
The "client" output format returns data in a simple newline-delimited list. String values are not delimited by quote characters, and list items are delimited by commas (including a comma after the final element). The format of each line is name=value
, where name
is the name of the variable being returned, and value
is the value of the variable. No whitespace characters are inserted between name
, =
, and value
.
The order in which the fields are returned is not guaranteed, and it should not be assumed that all tags will be present or in any particular order.
The MIME type of the client output is 'text/plain'.
The result of the browse command http://slugwiki.mit.edu:8080/browse?output=client&dir=/P/Porcupine%20Tree
dir0=/P/Porcupine Tree/Coma Divine dir1=/P/Porcupine Tree/Spiral Circus dir2=/P/Porcupine Tree/In Absentia dir3=/P/Porcupine Tree/Insignificance dir4=/P/Porcupine Tree/Lightbulb Sun dir5=/P/Porcupine Tree/Metanoia dir6=/P/Porcupine Tree/On the Sunday of Life dir7=/P/Porcupine Tree/Recordings file0=/P/Porcupine Tree/Chloroform (Instrumental Rough Mix).mp3 file1=/P/Porcupine Tree/Disappear (Demo Version 2.0).mp3 file2=/P/Porcupine Tree/Is Not.mp3 file3=/P/Porcupine Tree/2003 Intro Music.mp3 file4=/P/Porcupine Tree/Even Less, Part 2.mp3 file5=/P/Porcupine Tree/London.mp3 success=true
The result of the playlists command http://slugwiki.mit.edu:8080/playlists?output=client
ids=32,30,31, success=true
The result of the players command with an invalid playerId http://slugwiki.mit.edu:8080/player?output=client&playerId=2
success=false comment=playerId not found success=true
HTML
When dumping data as HTML, bemix creates pages that can also be used to issue commands to the server. For instance, a browse page generates a link for each directory that is being displayed that points to the browse page for that subdirectory.
HTML output is meant to be used through a webbrowser. If you're interested in manipulating the bemix server through some other means, use the "client" output format described above.
Todo
The code base needs to be cleaned up substantially before the 1.0 release of the software. Currently an alpha version, the code works, but is messy.
Beta Version
The beta version will contain various improvements over the current version. This version will add support for slave clients, and will permit slaves to drop in and out at will.
bemix-server
- The handler for the bemix server parses the incoming url into an action, an output type, and a dictionary of variables. These are then given to manager.ServerManager.
- Supports bemix-slaves from dropping in and out dynamically, so computers can be turned off and on without restarting the bemix system
- Deals with browsing and playlist administration as well
manager.ServerManager
- Maintains a list of available bemix-slave instances, their available players, and the available playlists
- Contains a single method
performAction(action, output="client", vars={})
that calls the appropriate method in the many command classes
bemixlib
- Contains methods that are used by manager.ServerManager to perform actions that bemix-server responds to (methods accept an output type and the vars dictionary)
bemix-slave
- All slaves must have access to the identical music shares (achieved externally via samba, afs, etc)
- A running http server that responds to messages from the bemix-server it is registered with only (done via IP address checking)
- Maintains internal state of volume, secondsElapsed, secondsTotal, status, index, and playlistId
- Must query bemix-server to get the filename and index of the next song it is to play (performed through queries such as playlist-next?playerId=0&slaveManagerId=2&output=slave which returns something like "song=/P/Porcupine Tree/London.mp3\nindex=5\n")
Overall Improvements
- create a SlaveManager to drop-in for the normal manager to be used in the slaves
- replace the current dispatching in bemix-server with a model that passes in all of the variables
- find a python module to parse the query string in a URL (rather than the current homebrew method)
- replace the current server+players & slaveServer+players with server+slavePlayers, where slavePlayers are created on both the local and remote machines -- might create too many open ports; could create a slave manager that has the open port and works like current server (controls the various local players itself), doesn't have to be done over http?
See Also
- Bemix - Overview information about the Bemix music system