JavaScript playlist (JSPL)
Introduction
A playlist is a sequence (ordered list) of media tracks (i.e., songs and movies) to specify one's favorite tracks and their order. In general, a playlist stores a sequence of file names (either in relative or absolute paths). For example, an M3U playlist is mere a text file in which each line describes a reference (i.e., a file name in relative or absolute path) to a media track. Therefore, playlists are static in the sense that the user must revise them to change the track list or sequence. Although it is useful to represent one's best tracks in his or her collection, some might want to organize playlist automatically with some rules, for example,
- All tracks in the player arranged in a random order;
- All tracks performed by the Beatles;
- All tracks with genre "Rock";
- Top 100 tracks played frequently in the player.
We call such playlists described by the rules as dynamic playlists. Dynamic playlists can be achieved by introducing a user interface (e.g., GUI form) to specify artist name, genre name, playback count, etc. However, such approach lacks the versatility: a user might not be satisfied with a predefined GUI form if he or she wants a playlist with more complicated rules. For this reason, we introduce a scripting language in which a user can access to the media information of all tracks, choose necessary tracks based on the media information, and arrange the tracks in an arbitrary order. EasyPMP employs JavaScript as the scripting language for describing dynamic playlists.
JavaScript is best known as the scripting language for dynamic web contents and has a great number of users. Some might presume that JavaScript is just for web contents, but it is also used as a scripting language embedded in other applications, e.g., Virtual Reality Modeling Language (VRML), Scalable Vector Graphics (SVG), etc. Moreover, JavaScript has a powerful language specification for describing dynamic playlist, featuring:
- Object Oriented Programing (OOP)
- Useful built-in objects such as Array, Math, Object, String, etc.
- Unicode support in String object.
- Regular Expression (RegExp) object.
The function to be implemented: main
Any JavaScript playlist must implement function main. This function chooses tracks to be included in the target playlist(s) from media information of all tracks in a portable player. For generating a single playlist, the function should return a JavaScript Array instance that represents a sequence (ordered list) of tracks. For generating multiple playlists, the function should return a JavaScript Object instance storing associations from playlist names to their corresponding track sequences. The application such as EasyPMP sets the necessary arguments (e.g., media information) to the function, receives the JavaScript value from the main function, and generates the corresponding playlist(s).
The following describes a skeleton code implementing the minimum components for a valid JSPL, not including any tracks to the corresponding plylist.
/**
* A skeleton code for JSPL.
* @param media The array of media information of all tracks.
* @param src The full-path name of this JSPL file.
* @return A JavaScript array (for singple playlist);
* a JavaScript object (for multiple playlists).
*/
function main(media, src)
{
}
Content of a playlist is determined by the array returned by the function main. For example, if an implementation collects tracks by a specific artist, the corresponding playlist will have the tracks by the artist. The primary task of the main function is to choose a set of preferred tracks by implementing a conditional expression. The following code extracts tracks performed by the Beatles.
/* JSPL with all tracks by Beatles. */
function main(media, src)
{
// Create a JavaScript array representing a playlist.
var pl = new Array();
for (var i = 0;i < media.length;++i) {
var track = media[i];
// Add the current track if the artist name is "Beatles".
if (track.artist == "Beatles") {
pl.push(track);
}
}
// Return the playlist.
return pl;
}
In addition, the playback order of a playlist is determined by the order of tracks stored in the JavaScript Array object. For example, if a returned array contains all tracks in a portable player in a random order, the corresponding playlist will be a random playlist with all tracks. The function is responsible for sorting tracks in a preferred order, e.g., numerical order of track numbers, alphabetical order of track titles, etc. The following code extracts all tracks in a player and arranges them in an alphabetical order of track titles. The sorting implementation can be simplified by using Playlist class "playlist.js" (described later).
/* JSPL with all tracks in an alphabetical order. */
function comp_title(x, y)
{
return ((x.title)>(y.title))-((x.title)<(y.title));
}
function main(media, src)
{
// Create a JavaScript array representing a playlist.
var pl = new Array();
// Add all tracks to the playlist.
for (var i = 0;i < media.length;++i) {
pl.push(media[i]);
}
// Sort the playlist in an alphabetical order of track titles.
pl.sort(comp_title);
// Return the playlist.
return pl;
}
A JavaScript playlist can generate multiple playlists, e.g., playlists each of which represents tracks by an artist. The main function must return a JavaScript object storing mappings from playlist names and their playlist contents. According to the JavaScript language specification, any JavaScript object incorporates an associative array that associates attribute names and their values. In JavaScript playlist, an attribute name represents a playlist name, and its value corresponds to a JavaScript array storing the track list. The following JSPL collects the tracks performed by the same artist into a playlist and generates such playlists for all artists.
/* JSPL for organizing multiple playlists by artist names. */
function main(media, src)
{
// Container storing multiple playlists.
var pls = new Object();
for (var i = 0;i < media.length;++i) {
var track = media[i];
// Access to the playlist named as the artist name.
var pl = pls[track.artist];
// Create a new playlist if such playlist does not exist.
if (pl == undefined) {
pl = pls[track.artist] = new Array();
}
// Add this track to the playlist.
pl.push(track);
}
// Return the container of multiple playlists.
return pls;
}
Arguments to main function
Function main receives two arguments, media and src. The first argument media is an array of JavaScript objects each of which represents media information of a track in a portable player. Each element in the array has a set of attributes that represents the media information. A JavaScript playlist makes use of the attributes to extract preferred tracks and to arrange them in a preferred order. The following is the list of the attributes in each element.
Field name | Type | Description | Example |
---|---|---|---|
id | Integer | Unique identifier | 1 |
filename | String | Full-path name of the media file | D:\Music\Beatles\YellowSubmarine\02.mp3 |
title | String | Title name | Hey Bulldog |
artist | String | Artist name | Beatles |
composer | String | Composer of this track typically used for classical music. | John Lennon & Paul McCartney |
album | String | Album name | Yellow Submarine |
genre | String | Genre name | Rock |
codec | Integer | Codec identifier | Codec.OggVorbis |
track_number | Integer | Track number | 2 |
sample_rate | Integer | Sample rate in Hz | 44100 |
bitrate | Integer | Bitrate in bps | 128000 |
duration | Integer | Track length in seconds | 193 |
rating | Integer | Rating value | 5 |
play_count | Integer | The number of playback times | 3 |
update_timestamp | Integer | Timestamp of the last modified | 193 |
import_timestamp | Integer | Timestamp when this track was added to the database | 193 |
playback_timestamp | Integer | Timestamp when this track was played in the player | 193 |
The second argument src presents the full-path name of the current JavaScript playlist.
JSPL extension to ECMAScript
For debugging purpose, JSPL defines a global function print to display values. The function takes multiple arguments and outputs the argument values. The following code outputs artist, album, and title names of all tracks.
/* JSPL displaying track artists, albums, and titles. */
function main(media, src)
{
for (var i = 0;i < media.length;++i) {
var track = media[i];
print(track.artist, track.album, track.title);
}
}
JSPL defines a global function include to evaluate an external JavaScript file. This function is useful to modularize reusable components. The following code import "playlist.js" located in JSPL include path (typically "jspl" directory under EasyPMP executable).
include("playlist.js");
Codec object (defined as a global object)
Codec object defines codec identifiers used by codec field in media information. Following member variables are defined:
- MP3
- MPEG Audio Layer III
- OggVorbis
- Ogg Vorbis
- WAV
- Microsoft WAVE
- WMA
- Windows Media Audio
Playlist object (playlist.js)
Playlist class is a specialization of JavaScript Array for the management of a playlist. In addition to the member function in Array, Playlist class has two member functions, order and shuffle.
order function
Member function order is useful to arrange tracks in a playlist. Although JavaScript Array class has member function sort, it is boring to implement a comparison function to arrange tracks in a preferred order. The function arranges tracks by the field(s) specified by the argument(s). Each argument represents the field name to predicate the order of two tracks. Receiving "title" as an argument, for example, the function arranges tracks in a alphabetical (descending) order of title names. If the function receives an argument beginning with character '-', tracks are arranged in an ascending order.
If the values of two tracks are identical by a comparison with the field specified by the first argument, the function tries the next field specified by the second argument (if any). This process will be repeated while the values of two tracks are identical. If two tracks are identical even after comparing with all fields, the order is left undefined.
The following snippet arranges tracks in an alphabetical order of artist names. If artist names of two tracks are identical, the order is determined by the alphabetical order of album names. If artist and album names of two tracks are the same, the order is determined by the numerical order of track numbers.
include("playlist.js");
pl = new Playlist();
...
/* Arrange tracks by artists, albums, and track numbers. */
pl.order("artist", "album", "track_number");
The following code arranges tracks in a descending order of play counts, i.e., in a popularity order.
include("playlist.js");
pl = new Playlist();
...
/* Arrange tracks in a descending order of play counts. */
pl.order("-play_count");
shuffle function
Member function shuffle randomizes the order of tracks in a playlist. The function is useful to generate a shuffle playlist.
include("playlist.js");
pl = new Playlist();
...
/* Arrange tracks in a random order. */
pl.shuffle();
Examples
Artist.jspl
/*
* Artist playlist
*
* This JSPL extracts tracks performed by the artist whose name is
* is specified by either variable 'artist_name' or filename of this JSPL.
*
*/
// Change this to specify the artist name
var artist_name = "";
include("playlist.js");
function main(media, source)
{
// Check artist_name.
if (!artist_name) {
// Retrieve an artist name from the filename.
var begin = 0, end = source.length;
for (var i = 0;i < source.length;++i) {
if (source[i] == '\' || source[i] == '/')
begin = i;
if (source[i] == '.')
end = i;
}
artist_name = source.slice(begin, end);
}
// Convert the artist name to lower case.
artist_name = artist_name.toLowerCase();
var pl = new Playlist();
for (var i = 0;i < media.length;++i)
if (media[i].artist.toLowerCase() == artist_name)
pl.push(media[i]);
pl.order("album", "track_number");
return pl;
}
Artists.jspl
/*
* Artists playlist
*
* This JSPL generates multiple playlists each of which collects tracks
* performed by an artist.
*
*/
include("playlist.js");
function main(media, playlist)
{
var pls = new Object();
for (var i = 0;i < media.length;++i) {
var artist = media[i].artist.toLowerCase();
var pl = pls[artist];
if (pl == undefined)
pl = pls[artist] = new Playlist();
pl.push(media[i]);
}
for (var name in pls) {
var pl = pls[name];
pl.order("album", "track_number");
}
return pls;
}
Reference
- JavaScript - MDC: Documentation of the JavaScript language used by Gecko-based browsers (e.g., FireFox) and also by EasyPMP (libplaylist).
- SpiderMonkey: The JavaScript engine used by EasyPMP (libplaylist). It is Gecko's JavaScript engine written in C. It is used in various Mozilla products, and is available under MPL/GPL/LGPL tri-license.