The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Apache::MP3::Skin - A subclass of Apache::MP3::Playlist with the ability to "skin" the output using HTML::Template

SYNOPSIS

 # httpd.conf or srm.conf
 AddType audio/mpeg    .mp3 .MP3

 # httpd.conf or access.conf
 <Location /songs>
   SetHandler perl-script
   PerlHandler Apache::MP3::Skin
   PerlSetVar HomePath  /songs   # optional
   PerlSetVar DefaultSkin  default.tmpl   # required  

   # Without DefaultSkin being set to a valid file
   # Apache::MP3::Skin will be the same as Apache::MP3
 </Location>

DESCRIPTION

Apache::MP3::Skin subclasses Apache::MP3::Playlist enabling the use of skin files which are html files with special tags enabled by HTML::Template. See Apache::MP3 for details on installing and using.

CUSTOMIZING WITH SKINS

The whole purpose of this class is to allow custom GUIs to be built upon Apache::MP3::Playlist and subsequently Apache::MP3 itself. Skin are just html files that contain various tags in the form of <TMPL_blah,blah [ATTRIBUTE=value]>. The filename of the skin to be used is set with PerlSetVar DefaultSkin and can be overridden by "?skin=someskin.tmpl" in the query string. The skin file is first looked for in the directory of the request. For example, if you were to go to /Songs/Rock, Apache::MP3::Skin would first look for the skin file in /Songs/Rock. If it didn't find one there, it'd look in the directory set by PerlSetVar BaseDir (usually, /apache_mp3). In most cases you'll want to keep all your skins in the BaseDir, but it is possible to have a different skin for each directory.

Complete documentation on these tags can be found at HTML::Template, but enough to get you started follows.

<TMPL_VAR [ESCAPE="HTML" | ESCAPE="URL"] NAME=variable>

Tag is replace with the value of variable and optionally escaped making it html or url compliant.

<TMPL_IF NAME=variable> html here [ <TMPL_ELSE> more here ] </TMPL_IF>

The value or variable is evaluated, and if it is not empty or "0" it is considered true and "some html" is outputed to the browser. A <TMPL_ELSE> can optionaly be specified.

<TMPL_UNLESS NAME=variable> html here [ <TMPL_ELSE> more here ] </TMPL_UNLESS>

Similar to <TMPL_IF> but "html here" is outputed to the browser when variable is either "" or "0".

<TMPL_LOOP NAME=loop_name> do this </TMPL_LOOP>

This tag is more complicated the others, but basically it outputs "do this" multiple times. The number of times is determined by loop_name. Looping also changes the context of some variable. For example...

    <ul>
    <TMPL_LOOP NAME="MP3S"><li> <TMPL_VAR NAME="TITLE"> </TMPL_LOOP>
    </ul>
    

For each iteration of MP3S, TITLE will be different value.

Note: that variables can be used in TMPL_VAR, TMPL_UNLESS, and TMPL_IF, but not in TMPL_LOOP which requires a loop_name.

<TMPL_INCLUDE NAME="filename.tmpl">

This tag includes a template directly into the current template at the point where the tag is found. The included template contents are used exactly as if its contents were physically included in the master template.

The file specified can be a full path - beginning with a '/'. If it isn't a full path, the path to the enclosing file is tried first.

NESTING TEMPLATE TAGS

Yes. All the tags can be nested in almost any way. A few variables are limited to be inside certain loops, but that's the only restriction.

VARIABLES

Variables can be used in TMPL_VAR, TMPL_UNLESS, and TMPL_IF, and they are grouped below into for types: global, directory scoped, file scoped, and special loop variables. At last count there were 68 valid variables and some like PARAM_param and IS_SORT_sort can take many forms.

Global Variables

These variables' values do not change during the parsing of a template and available everywhere.

ADD_TO_PLAYLIST_ALL_SCRIPT

(string) JavaScript that will add all the MP3s currently listed on the page. Differs from ADD_TO_PLAYLIST_ALL_THIS_DIR_URL which is directory context sensitive.

ADD_TO_PLAYLIST_SELECTED_SCRIPT

(string) JavaScript to add the selected MP3S to the playlist.

ALLOW_DOWNLOAD

(1|0) Is streaming allowed? Set by PerlSetVar AllowDownload. It's good form to test if streaming is allowed before using variables like FETCH_URL which will not work.

ALLOW_STREAM

(1|0) Is streaming allowed? Set by PerlSetVar AllowStream. It's good form to test if streaming is allowed before using variables like PLAY_URL which will not work.

BASE_DIR

(string) path the base directory. Set by PerlSetVar BaseDir and defaulted to /apache_mp3.

CLEAR_ALL_PLAYLIST_URL

(string) URL to remove all the files from the playlist.

CLEAR_SELECTED_PLAYLIST_SCRIPT

(string) JavaScript to remove all the selected files from the playlist.

CONTAINS_PLAYLIST

(int) Return the number of songs on the current browsers playlist. Useful in TMPL_IF and TMPL_UNLESS tags as an empty playlist will contain 0 songs.

DIR

(string) The name of the directory that browser is looking at, similar to THIS_DIR except DIR's value does not change with the directory context.

HOME_LABEL

(string) The name of the top (or home) directory as defined by PerlSetVar.

HOME_PATH

(string) The hostname-relative PATH of the top (or home) directory as defined by PerlSetVar HomePath.

IS_SORT_method

(1|0) Is the current page being sorted by method where method is title, album, artist, etc.. Default is the sort method defined by PerlSetVar SortFields.

PARAM_param

(string) Returns the value of the param named 'param' from the URL's query string. For example if the current url is http://www.apachemp3.com/demo/pop?cartopen=yes then <TMPL_VAR NAME=PARAM_CARTOPEN> would return "yes". This can be useful for complicated skins that need to maintain state.

PLAY_ALL_PLAYLIST_URL

(string) URL to play all files in the playlist in order.

PLAY_ALL_SCRIPT

(string) Javascript that will play all the MP3s currently listed on page. Differs from PLAY_ALL_THIS_DIR_URL which is directory context sensitive.

PLAY_SELECTED_PLAYLIST_SCRIPT

(string) JavaScript that will play all the selected files in the playlist.

PLAY_SELECTED_SCRIPT

(string) Javascript that will play all selected songs.

SELECT_ALL_MP3S_SCRIPT

(string) JavaScript that checks all the checkboxes generated by <TMPL_VAR NAME=CHECKBOX> for MP3 files.

SELECT_ALL_PLAYLIST_SCRIPT

(string) JavaScript that checks all the checkboxes generated by <TMPL_VAR NAME=CHECKBOX> in the playlist.

SORT

(string) The current sort method. Returns "" if default, otherwise returns the field name currently being used to sort See Apache::MP3::Sorted for appropriate values. This is the same as PARAM_sort since the sort field is contained in the query string. Default sort is defined by PerlSetVar SortFiles.

SHUFFLE_ALL_PLAYLIST_URL

(string) URL to play all files in the playlist in a random order.

SHUFFLE_ALL_SCRIPT

(string) Javascript that will play all the MP3s currently listed on a page in a random order. Not directory context sensitive.

SHUFFLE_SELECTED_SCRIPT

(string) Javascript that will play all selected songs in a random order.

SKIN

(string) The current skin. Append to URLs in the form of ?skin=<TMPL_VAR NAME=SKIN> to maintain your skin if it is not the default. Useful in skins that require multiple files for framesets.

STREAM_TIMEOUT

(int) If anything but 0, this returns the number in seconds of how long a file will stream before timing out, otherwise returns an empty string. Set by PerlSetVar StreamTimeout. Used for demos or in cases when streaming an entire song would not be appropriate or illegal.

UNSELECT_ALL_MP3S_SCRIPT

(string) JavaScript that Unchecks all the checkboxes generated by <TMPL_VAR NAME=CHECKBOX> for all MP3 files.

UNSELECT_ALL_PLAYLIST_SCRIPT

(string) JavaScript that unchecks all the checkboxes generated by <TMPL_VAR NAME=CHECKBOX> in the playlist.

URL

(string) The URL path to DIR. Similar to THIS_URL but does not change with the directory context

Directory Scoped variables

These variables' values are determined by the current directory context. In the context of a directory loop -- PATH_FORWARD, PATH_BACKWARD, HOME_DIRS, and DIRS -- the values are those of directory for that loop iteration. Outside of loops, the directory context is that of the current browser request. These varables are available everywhere as there is always a directory context.

IS_LONG_PAGE

(1|0) Does the current directory have more files than the defined value in PerlSetVar LongList. Useful for adding a second set of buttons on a long page.

CONTAINS_MP3S

(int) Return the number of MP3s in the current directory. Useful in IF and UNLESS commands to test existance of any MP3s.

CONTAINS_DIRS

(int) Return the number of sub-directories in the current directory. Useful in IF and UNLESS commands to test existance of any sub-directories.

IS_HOME

(1|0) Is the current directory the top (or root) directory as defined by PerlSetVar HomePath.

COVER

(string) The full src path of an image. Returns an empty string if there is no image. The empty string is useful to test the existance of a cover image. The image is looked for has the filename set by PerlSetVar CoverImage (dafault is cover.jpg) in the current directory.

IS_DIR_INSIDE_THIS_DIR

(1|0) The key to understand this and the next two cryptic variables is to rember that DIR and THIS_DIR are other variables. If DIR, the directory the browser is looking at, is inside THIS_DIR. This is useful for determining when creating global navigation and you want a certain tab (THIS_DIR) to be highlighted if the browser is looking at a directory(DIR) beneath it.

IS_DIR_THIS_DIR

(1|0) Related to IS_DIR_INSIDE_THIS_DIR, this variable is 1 when the two are the same.

THIS_DIR

(string) The name of the current directory.

THIS_URL

(string) The path part of the URL for the current directory. Never just './' because the current directory in a loop, may not be the directory the browser is looking at. Use URL to return constant equivilant of "./".

PARENT_DIR

(string) The name of the parent directory, if the current directory is not the HomePath; otherwise, an empty string.

PARENT_URL

(string) The path part of the URL for parent directory, if the current directory is not the HomePath; otherwise, an empty string.

PLAY_ALL_THIS_DIR_URL

(string) The URL to Play All songs in a the current directory.

SHUFFLE_ALL_THIS_DIR_URL

(string) The URL to Shuffle and Play All songs in the current directory.

PLAY_ALL_THIS_DIR_RECURSIVE_URL

(string) The URL to Play All songs in a the current directory and it directories recursively.

SHUFFLE_ALL_THIS_DIR_RECURSIVE_URL

(string) The URL to Shuffle and Play All songs in the current directory and its directories recursively.

File Scope Variables

These may be used inside the MP3S loop, and also in MP3_AND_DIRS and DIRS_AND_MP3S when the iteration is an MP3 -- Test with IS_MP3. Most but not of them are also available inside PLAYLIST. Those are noted.

ALBUM

(string) The album name. Also available in PLAYLIST loop.

ARTIST

(string) The artist name. Also available in PLAYLIST loop.

BITRATE

(string) Streaming rate of song in kbps. Also available in PLAYLIST loop.

COMMENT

(string) The comment field. Also available in PLAYLIST loop.

DESCRIPTION

(string) Description, as controlled by DescriptionFormat. Also available in PLAYLIST loop.

DURATION

(string) Duration of the song in minute, second format. Also available in PLAYLIST loop.

FILENAME

(string) The physical name of the .mp3 file. Also available in PLAYLIST loop.

GENRE

(string) The genre. Also available in PLAYLIST loop.

SAMPLERATE

(string) Sample rate, in KHz. Also available in PLAYLIST loop.

SECONDS

(string) Duration of the song in seconds. Also available in PLAYLIST loop.

TITLE

(string) The title of the song. Also available in PLAYLIST loop.

TRACK

(string) The track number. Also available in PLAYLIST loop.

CHECKBOX

(string) The HTML for the form checkbox where the user can select a song. Related to PLAY_SELECTED_SCRIPT and other _SCRIPT variables. Also available in PLAYLIST loop.

FETCH_URL

(string) The file's URL to fetch it from. May be disabled with PerlSetVar AllowDownload no. Also available in PLAYLIST loop.

PLAY_URL

(string) The file's URL to stream it from. May be disabled with PerlSetVar AllowStream. Also available in PLAYLIST loop.

REMOVE_FROM_PLAYLIST_URL

(string) The URL to remove the file from the playlist. Only available inside PLAYLIST loop.

ADD_TO_PLAYLIST_URL

(string) The URL to add the current file to the users playlist. Not available inside PLAYLIST loop.

Special Loop Variables

Inside a loop these varables are also available

__COUNT__

(int) Starting with 1, this is the loop count. It increases by 1 with each iteration.

__COUNT_BASE_ZERO__

(int) Useful for building JavaScript Object and Arrays, this is __COUNT__ minus 1.

__FIRST__

(1|0) Returns 1 if __COUNT__ is 1, otherwise it returns 0. More simply, it's 1 for the first iteration only. Implemented by HTML::Template.

__LAST__

(1|0) Returns 1 on the last iteration only. Implemented by HTML::Template.

__INNER__

(1|0) If an iteration is not first and not last, than __INNER__ is 1. Implemented by HTML::Template.

__FIRST_COL_x__

(1|0) Where x is an integer. For __FIRST_COL_3__, 1 would be returned on __COUNT__ values: 1, 4, 7, 10, etc.

__LAST_COL_x__

(1|0) Similar to __FIRST_COL_x__ but given x is 3, __LAST_COL_3__ would return 1, on the following iterations: 3, 6, 9, 12, etc.

__INNER_COL_x__

If an iteration is not a __FIRST_COL_x__ or a __LAST_COL_x__ it is an __INNER_COL_x__.

__ODD__

(1|0) Returns 1 on odd __COUNT__ values.

__EVEN__

(1|0) Returns 0 on even __COUNT__ values.

IS_MP3

(1|0) Useful in LOOP_MP3S_AND_DIRS and LOOP_DIRS_AND_MP3S this will return 1 if the current loop iteration is an mp3.

IS_DIR

(1|0) Useful in LOOP_MP3S_AND_DIRS and LOOP_DIRS_AND_MP3S this will return 1 if the current loop iteration is a directory.

About Variables Ending in _URL or _SCRIPT

Many of the TMPL_VAR variables below return URLs or JavaScript functions. Variables that return URLs, always end with _URL. This value can be used as an href in an anchor tag. Or be included in a javascript event like onClick. Here's an example of use in a form button.

<BUTTON onClick="location=<TMPL_VAR NAME=SOME_URL>;">

and then a variable ending in _SCRIPT would be like this:

<BUTTON onClick="<TMPL_VAR NAME=SOME_SCRIPT>">

to use a _SCRIPT as part of a url do something like this:

<a href="javascript: <TMPL_VAR NAME=SOME_SCRIPT>">

LOOP NAMES

A variety of loops are possible, and all can be nested inside of each other for some interesting and sometimes useless effects. Note that when looping through a series of directories, the current directory context changes. So multiple nested DIRS loops would result in a directory tree because DIRS loops through the current directory context, and inside iterations change that same context.

Valid loop names are:

PATH_FORWARD

Iterate through the path from the top (or home) directory as defined by PerlSetVar to the current Directory. Useful for making breadcrumb trails.

PATH_BACKWARD

Same as PATH_FORWARD but the loops starts with the current directory and goes up the file tree to the top (or home) directory.

HOME_DIRS

Loop through all the directories in the top (or home) directory. Useful for creating persistent global nav.

MP3S

Loop through all the MP3s in the current directory.

DIRS

Loop through all the DIRs in the current directory.

PLAYLIST

Loop through all the MP3s in the current browser's playlist.

DIRS_AND_MP3S

Loop through all the directories and then all the MP3s in the current directory. Use the IS_DIR variable to test whether a given iteration is a directory or MP3.

MP3S_AND_DIRS

Same and DIRS_AND_MP3S except all the MP3s come before directories.

ABOUT FRAMES

A skin can be composed of multiple template files. The default template should contain the framset and the source's for each from should end with "?skin=thisframe.tmpl" where thisframe.tmpl is the name of the file to be used to skin that frame. Links inside thisframe.tmpl will also need to end in "?skin=thisframe.tmpl" to maintain their look.

ABOUT FORMS

A open form and closing form tag is automatically added to every page. Do not include any forms tags in your skin files. You can include form input fields and they will be part of the apache_mp3_skin form. Use document.apache_mp3_skin to refer to the form object in any JavaScript that you have.

METHODS

Apache::MP3::Skin overrides the following methods:

list_directory()

Checks to see if this is a skin file, if not hands off to SUPER::list_directory(). If there is a skin files, gets it, processes it, adds in the <SCRIPT> and <FORM> tags and the prints it.

run()

Looks for the "Shuffle Selected" parameter and handles the request if there is one. Otherwise, sends to SUPER::run().

process_playlist()

Same as SUPER::process_playlist with only one line changed. If there is an 'Add to Playlist' param. file param values that begin with '/' are not prepended with the current uri and are treated as document root relative.

And adds the following:

set_template_params()

Called by list_directory, this begins the process of filling in the template params. set_template_params sets the current directory context to the uri of the request and calls set_dir_context_params.

set_dir_context_params()

Loops through the parameters required for the template, sending special loop variables to set_loop_params and most of the rest to set_context_params

set_mp3_context_params()

Called by loop_mp3s and loop_playlist, set_mp3_context_params iterates through the parameters being requested in each loop iteration. Those that are file context are handled, special loop variables are sent to set_loop_params, and the rest go to set_context_params.

set_loop_params()

Called by set_mp3_context_params and set_dir_context_params, set_loop_params takes the name of a special loop variable, and the current loop count and return the appropriate value for the special loop variable.

set_context_params()

The workhorse. set_context_params takes in the name of the current parameter being queried along with context information and returns the appropriate values. Handles all global variables, directory context variables, and loops.

loop_dirs_and_mp3s()

Called by set_context_params for the loops DIRS_AND_MP3S and MP3S_AND_DIRS. Loops through all the dirs and mp3s calling either iterate_dirs() or set_mp3_context_params() for each as necessary.

loop_home_dirs()

Called by set_context_params for the HOME_DIRS loop. Gets a list of directories in HomePath and send it to iterate_dirs().

loop_dirs()

Called by set_context_params for the DIRS loop. Gets a list of directories in the current directory and sends it to iterate_dirs().

loop_path()

Called by set_context_params for PATH_FORWARD and PATH_BACKWARDS loops. Gets a list of directories from the HomePath to the current directory context. Reverses them if necessary. And then sends them to iterate_dirs().

iterate_dirs()

Called by loop_dirs_and_mp3s, loop_dirs, loop_home_dirs, and loop_path. Takes a list of directories and sends each to set_context_params().

loop_mp3s()

Called by set_context_params for MP3S loops. Iterates through the .mp3 files in the current directory context sending each to set_mp3_context_parameters().

loop_playlist()

Called by set_context_params for MP3S loops. Iterates through the files in the browser's playlist. Sends each to set_mp3_context_parameters().

TO DO

Support Playlist Files (.m3u)

Support for playlist files (.m3u). These are supported by Apache::MP3, and ignored by Apache::MP3::Skin. I skipped over this because 1. I don't use them so it was the last thing I need for my own skins 2. I'm not sure how to handle them when looping. Include them in which loops? 3. I'd really like to be able to loop the contents of the .m3u file to allow the skin access to the contents.

Two More Variables

Directory context sensitive variables that where planned implemented, and then unimplemented.

    ADD_TO_PLAYLIST_ALL_THIS_DIR_URL
        *** Not Implemented ***
        When completed this will add the current directory's MP3s to the Playlist.  For now, use 
        ADD_PLAYLIST_ALL_SCRIPT which in most cases will have a very similar outcome.  This is 
        tricky because the current methods of Apache::MP3::Playlist always Add the directory 
        the browser is looking at which may not always be the current directory context. 
    
    ADD_TO_PLAYLIST_ALL_THIS_DIR_RECURSIVE_URL
        *** Not Implemented ***
        When completed this will the current directory's MP3s to the Playlist recursing
        through sub-directories.  Not Implemented for the same reasons as  
        ADD_TO_PLAYLIST_ALL_THIS_DIR_URL.
RegEx Attributes

Possibly patch or subclass, HTML::Template adding a generalized RegEx attribute to TMPL_VAR tags. Sometimes Titles needs more than just HTML escaping they need "_" changed to " " or you may want to force something to all caps or truncate to a certain length.

BUGS

Sure. Visit http://www.workingdemo.com/bugs.html for the lastest. Create an account or simply login in as guest/guest to report bugs.

AUTHOR

Copyright 2000, Robert Graff <rgraff@apachemp3.com>.

Visit me at workingdemo.com and robertscloset.com

This module is distributed under the same terms as Perl itself. Feel free to use, modify and redistribute it as long as you retain the correct attribution.

ACKNOWLEDGEMENTS

Lincoln Stein <lstein@cshl.org> created Apache::MP3, Apache::MP3::Sorted, and Apache::MP3::Playlist upon which this module is built. He also co-authored a great book which taught me enough about mod_perl and Apache to attempt this.

Sam Tregar, sam@tregar.com wrote HTML::Template which set the stage quite nicely for creating this module, as I didn't have to worry about parsing the template just filling it.

SEE ALSO

Apache::MP3, MP3::Info, Apache HTML::Template