NAME

Apache::MP3 - Generate streamable directories of MP3 files

SYNOPSIS

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

# httpd.conf or access.conf
<Location /songs>
  SetHandler perl-script
  PerlHandler Apache::MP3
</Location>

 # Or use the Apache::MP3::Sorted subclass to get sortable directory listings
<Location /songs>
  SetHandler perl-script
 PerlHandler Apache::MP3::Sorted
</Location>

A demo version can be browsed at http://www.modperl.com/Songs/.

DESCRIPTION

This module makes it possible to browse a directory hierarchy containing MP3 files, sort them on various fields, download them, stream them to an MP3 decoder like WinAmp, and construct playlists. The display is configurable and subclassable.

ppNOTE: This version of Apache::MP3 is substantially different from the pre-2.0 version described in The Perl Journal. Specifically, the format to use for HREF links has changed. See Linking for details.

Installation

This section describes the installation process.

1. Prequisites

This module requires mod_perl and MP3::Info, both of which are available on CPAN.

2. Configure MIME types

Apache must be configured to recognize the mp3 and MP3 extensions as MIME type audio/mpeg. Add the following to httpd.conf or srm.conf:

AddType audio/mpeg mp3 MP3
3. Install icons and stylesheet

This module uses a set of icons and a cascading stylesheet to generate its song listings. By default, the module expects to find them at the url /apache_mp3. Create a directory named apache_mp3 in your document root, and copy into it the contents of the apache_mp3 directory from the Apache-MP3 distribution.

You may change the location of this directory by setting the BaseDir configuration variable. See the Customizing section for more details.

4. Set Apache::MP3 or Apache::MP3::Sorted as handler for MP3 directory

In httpd.conf or access.conf, create a <Location> or <Directory> section, and make Apache::MP3 the handler for this directory. This example assumes you are using the URL /Songs as the directory where you will be storing song files:

<Location /Songs>
  SetHandler perl-script
  PerlHandler Apache::MP3
</Location>

If you would prefer an MP3 file listing that allows the user to sort it in various ways, set the handler to use the Apache::MP3::Sorted subclass instead. This is recommended

5. Load MP3::Info in the Perl Startup file (optional)

For the purposes of faster startup and memory efficiency, you may load the MP3::Info module at server startup time.

6. Set up MP3 directory

Create a directory in the web server document tree that will contain the MP3 files to be served. The module recognizes and handles subdirectories appropriately. I suggest organizing directories by artist and or CD title. For directories containing multiple tracks from the same CD, proceed each mp3 file with the track number. This will ensure that the directory listing sorts in the right order.

7. Set up an information cache directory (optional)

In order to generate its MP3 listing, Apache::MP3 must open each sound file, extract its header information, and close it. This is time consuming, particularly when recursively generating playlists across multiple directories. To speed up this process, Apache::MP3 has the ability cache MP3 file information in a separate directory area.

To configure this, create a directory that the Web server sill have write access to, such as /usr/tmp/mp3_cache, and add this configuration variable to the <Location> directive:

PerlSetVar  CacheDir       /usr/tmp/mp3_cache

If the designated does not exist, Apache::MP3 will attempt to create it, limited of course by the Web server's privileges.

Open up the MP3 URL in your favorite browser. If things don't seem to be working, checking the server error log for informative messages.

CUSTOMIZING

Apache::MP3 can be customized in three ways: (1) by changing per-directory variables; (2) changing settings in the Apache::MP3 cascading stylesheet; and (3) subclassing Apache::MP3 or Apache::MP3::Sorted.

Per-directory configuration variables

Per-directory variables are set by PerlSetVar directives in the Apache::MP3 <Location> or <Directory> section. For example, to change the icon displayed next to subdirectories of MP3s, you would use PerlSetVar to change the DirectoryIcon variable:

PerlSetVar DirectoryIcon big_cd.gif

This table summarizes the configuration variables. A more detailed explanation of each follows.

CONFIGURATION VARIABLES FOR PerlSetVar

Name                  Value	        Default
----                  -----            -------

AllowDownload	       yes|no		yes
AllowStream	       yes|no		yes
CheckStreamClient     yes|no		no
ReadMP3Info	       yes|no		yes

StreamTimout          integer          0
SubdirColumns	       integer		3
LongList	       integer		10
Fields                list             title,artist,duration,bitrate
SortField             field name       filename
PathStyle             Staircase|Arrows Staircase

BaseDir	       URL		/apache_mp3
CacheDir              path             -none-
HomeLabel	       string		"Home"
StreamBase            URL              -none-

Stylesheet	       URL		apache_mp3.css
TitleIcon	       URL		cd_icon.gif
DirectoryIcon	       URL		cd_icon_small.gif
SongIcon	       URL		sound.gif
ArrowIcon	       URL		right_arrow.gif
ParentIcon	       URL		back.gif    # defunct
AllowDownload yes|no

You may wish for users to be able to stream songs but not download them to their local disk. If you set AllowDownload to "no", Apache::MP3 will not generate a download link for MP3 files. It will also activate some code that makes it inconvenient (although not impossible) for users to download the MP3s.

The arguments "yes", "no", "true" and "false" are recognized. The default is "yes".

AllowStream yes|no

If you set AllowStream to "no", users will not be able to stream songs or generate playlists. I am not sure why one would want this feature, but it is included for completeness. The default is "yes."

CheckStreamClient yes|no

Setting CheckStreamClient to "yes" enables code that checks whether the client claims to be able to accept streaming MPEG data. This check isn't foolproof, but supports at least the most popular MP3 decoders (WinAmp, RealPlayer, xmms, mpg123). It also makes it harder for users to download songs by pretending to be a streaming player.

The default is "no".

ReadMP3Info yes|no

This controls whether to extract field information from the MP3 files. The default is "yes".

If "no" is specified, all fields in the directory listing will be blank except for filename and description, which will both be set to the physical filename of the MP3 file.

StreamTimeout integer

For demo mode, you can specify a stream timeout in seconds. Apache::MP3 will cease streaming the file after the time specified. Because this feature does not take into account TCP-buffered song data, the actual music may not stop playing until five or 10 seconds later.

SubdirColumns integer

The number of columns in which to display subdirectories (the small "CD icons"). Default 3.

LongList integer

The number of lines in the list of MP3 files after which it is considered "long". In long lists, the control buttons are placed at the top as well as at the bottom of the table. Defaults to 10.

Fields title,artist,duration,bitrate

Specify what MP3 information fields to display in the song listing. This should be a list delimited by commas, "|" symbols, or any other non-word character.

The following are valid fields:

Field        Description

title        The title of the song
artist       The artist
album	 The album
track	 The track number
genre        The genre
description	 Description in the form "title - artist (album)"
comment      The comment field
duration     Duration of the song in hour, minute, second format
seconds      Duration of the song in seconds
kbps         Streaming rate of song in kilobits/sec
filename	 The physical name of the .mp3 file

Note that MP3 rip and encoding software differ in what fields they capture and the exact format of such fields as the title and album.

SortField field

This configuration variable sets the default sort field when file listings are initially displayed. Any of the MP3 information fields listed in the previous section are allowed. By default, the sort direction will be alphabetically or numerically ascending. Reverse this by placing a "-" in front of the field name. Examples:

PerlSetVar SortField  title      # sort ascending by title
PerlSetVar SortField  -kbps      # sort descending by kbps

NOTE: Sorting is only implemented in the Apache::MP3::Sorted module.

PathStyle Staircase|Arrows

Controls the style with which the parent directories are displayed. The options are "Staircase" (the default), which creates a staircase-style display (each child directory is on a new line and offset by 0.3 em). The other is "Arrows", in which the entire directory list is on a single line and separated by graphic arrows. Try them both and choose the one you prefer.

BaseDir URL

The BaseDir variable sets the URL in which Apache::MP3 will look for its icons and stylesheet. You may use an absolutea local or remote URL. Relative URLs are not accepted.

The default is "/apache_mp3."

CacheDir path

This variable sets the directory path for Apache::MP3's cache of MP3 file information. This must be an absolute path in the physical file system and be writable by Apache.

Stylesheet URL

Set the URL of the cascading stylesheet to use, "apache_mp3.css" by default. If the URL begins with a slash it is treated as an absolute URL. Otherwise it is interpreted as relative to the BaseDir directory.

TitleIcon URL

Set the icon displayed next to the current directory's name, "cd_icon.gif" by default. In this, and the other icon-related directives, relative URLs are treated as relative to BaseDir.

The default is "cd_icon.gif".

DirectoryIcon URL

Set the icon displayed next to subdirectories in directory listings, "cd_icon_small.gif" by default.

SongIcon URL

Set the icon displayed at the beginning of each line of the MP3 file list, "sound.gif" by default.

ArrowIcon URL

Set the icon used for the arrows displayed between the components of the directory path at the top of the directory listing.

ParentIcon URL

This configuration variable is no longer used, but remains for backward compatibility.

HomeLabel string

This is the label for the link used to return to the site's home page. You may use plain text or any fragment of HTML, such as an <IMG> tag.

StreamURL URL

A URL to use as the base for streaming. The default is to use the same host for both directory listings and streaming. This may be of use for transparent reverse proxies.

Example:

If the song requested is http://www.foobar.com/Songs/Madonna_live.m3u?stream=1

and StreamURL is set to http://streamer.myhost.net, then the URL placed in the playlist will be

http://streamer.myhost.net/Songs/Madonna_live.m3u?stream=1

A more general rewrite facility is not available, but might be added if requested.

HelpURL URL:widthxheight

The URL of the page to display when the user presses the "Quick Help Summary" link at the bottom of the page. In the current implementation, the module pops up a plain window containing a marked-up GIF of a typical page. You can control the size of this page by adding ":WxH" to the end of the URL, where W and H are the width and height, respectively.

Default: apache_mp3_help.gif:614x498

Stylesheet-Based Configuration

You can change the appearance of the page by changing the cascading stylesheet that accompanies this module, apache_mp3.css. The following table describes the tags that can be customized:

Class Name           Description
----------           ----------

BODY                 General defaults
H1                   Current directory path
H2                   "CD Directories" and "Song List" headings
TR.title             Style for the top line of the song listing
TR.normal            Style for odd-numbered song listing lines
TR.highlight         Style for even-numbered song listing lines
.directory           Style for the title of the current directory
.subdirectory        Style for the title of subdirectories
P                    Ordinary paragraphs
A                    Links
INPUT                Fill-out form fields

Subclassing this Module

For more extensive customization, you can subclass this module. The Apache::MP3::Sorted module illustrates how to do this.

Briefly, your module should inherit from Apache::MP3 (or Apache::MP3::Sorted) either by setting the @ISA package global or, in Perl 5.6 and higher, with the use base directive. Your module should define a handler() subroutine that creates a new instance of the subclass and immediately calls its handle_request() method. This illustrates the idiom:

package Apache::MP3::EvenBetter;
use strict;
use Apache::MP3::Sorted;
use base Apache::MP3::Sorted;

sub handler {
  __PACKAGE__->handle_request(@_);
}

# new and overridden methods, etc....
1;

I decided not to use Apache method handlers for this after I discovered that I had to completely stop and relaunch the server every time I made a change to the module (even with PerlFreshRestart on).

See The Apache::MP3 API below for more information on overriding Apache::MP3 methods.

Linking to this module

You may wish to create links to MP3 files and directories manually. The rules for creating HREFs are different from those used in earlier versions of Apache::MP3, a decision forced by the fact that the playlist format used by popular MP3 decoders has changed.

The following rules apply:

Download an MP3 file

Create an HREF using the unchanged name of the MP3 file. For example, to download the song at /songs/Madonna/like_a_virgin.mp3, use:

<a href="/Songs/Madonna/like_a_virgin.mp3">Like a Virgin</a>
Stream an MP3 file

Replace the MP3 file's extension with .m3u and add the query string "play=1". Apache::MP3 will generate a playlist for the streaming MP3 decoder to load. Example:

<a href="/Songs/Madonna/like_a_virgin.m3u?play=1">
        Like a streaming Virgin</a>
Stream a directory

Append "/playlist.m3u?Play+All=1" to the end of the directory name:

<a href="/Songs/Madonna/playlist.m3u?Play+All=1">Madonna Lives!</a>

The capitalization of "Play All" is significant. Apache::Mp3 will generate a playlist containing all MP3 files within the directory.

Stream a directory heirarchy recursively

Append "/playlist.m3u?Play+All+Recursive=1" to the end of the directory name:

<a href="/Songs/HipHop/playlist.m3u?Play+All+Recursive=1">Rock me</a>

The capitalization of "Play All Recursive" is significant. Apache::MP3 will generate a playlist containing all MP3 files within the directory and all its subdirectories.

Shuffle and stream a directory

Append "/playlist.m3u?Shuffle+All=1" to the end of the directory name:

<a href="/Songs/HipHop/playlist.m3u?Shuffle+All">Rock me</a>

Apache::MP3 will generate a playlist containing all MP3 files within the directory and all its subdirectories, and then randomize its order.

Shuffle an entire directory heirarchy recursively

Append "/playlist.m3u?Shuffle+All+Recursive=1" to the end of the directory name:

<a href="/Songs/HipHop/playlist.m3u?Shuffle+All+Recursive=1">Rock me</a>

Apache::MP3 will generate a playlist containing all MP3 files within the directory and all its subdirectories, and then randomize its order.

Play a set of MP3s within a directory

Append "/playlist.m3u?Play+Selected=1;file=file1;file=file2..." to the directory name:

<a
href="/Songs/Madonna/playlist.m3u?Play+Selected=1;file=like_a_virgin.mp3;file=evita.mp3">
Two favorites</a>

Again, the capitalization of "Play Selected" counts.

Display a sorted directory

Append "?sort=field" to the end of the directory name, where field is any of the MP3 field names:

<a href="/Songs/Madonna/?sort=duration">Madonna lives!</a>

The Apache::MP3 API

The Apache::MP3 object is a blessed hash containing a single key, r, which points at the current request object. This can be retrieved conveniently using the r() method.

Apache::MP3 builds up its directory listing pages in pieces, using a hierarchical scheme. The following diagram summarizes which methods() are responsible for generating the various parts. It might help to study it alongside a representative HTML page:

list_directory()
-------------------------  page top --------------------------------
   directory_top()

   <CDICON> <DIRECTORY> -> <DIRECTORY> -> <DIRECTORY>
   [Shuffle All] [Stream All]

   list_subdirs()

        subdir_list_top()
        ------------------------------------------------------------
        <CD Directories (6)>

        subdir_list()
              <cdicon> <title>   <cdicon> <title>  <cdicon> <title>
              <cdicon> <title>   <cdicon> <title>  <cdicon> <title>

        subdir_list_bottom()  # does nothing
        ------------------------------------------------------------

   list_mp3s()
        mp3_list_top()
        ------------------------------------------------------------
        <Song List (4)>

        mp3_list()
            mp3_list_top()
              mp3_table_header()
                 <Select>                  Title          Kbps

              format_song() # called for each row
                 <icon>[] [fetch][stream]  Like a virgin  128
                 <icon>[] [fetch][stream]  American Pie   128
                 <icon>[] [fetch][stream]  Santa Evita     96
                 <icon>[] [fetch][stream]  Boy Toy        168

            mp3_list_bottom()
              [Play Selected] [Shuffle All] [Play All]

   directory_bottom()
-------------------------  page bottom -----------------------------

Method Calls

This section lists each of the Apache::MP3 method calls briefly.

$response_code = handler($request)

This is a the standard mod_perl handler() subroutine. It is a simple front-end to Apache::MP3->handle_request(). As described above under Subclassing this Module, you will need to provide a minimal handler() subroutine for each subclass you create.

$mp3 = Apache::MP3->new(@args)

This is a constructor. It stores the passed args in a hash and returns a new Apache::MP3 object. If a single argument is passed it assumes that it is an Apache::Request object and stores it under the key "r". You should not have to modify this method.

$response_code = Apache::MP3->handle_request(@args)

This is the other constructor. It calls new() to create a new $mp3 argument and immediately calls its run() method. You should not have to modify this method.

$request = $mp3->r()

Return the stored request object.

$response_code = $mp3->run()

This is the method that interprets the CGI parameters and dispatches to the routines that draw the directory listing, generate playlists, and stream songs.

$response_code = $mp3->process_directory($dir)

This is the top-level method for generating the directory listing. It performs various consistency checks on the passed directory URL and returns an Apache response code. The list_directory() method actually does most of the formatting work.

$response_code = $mp3->download_file($file)

This method is called to download a file (not stream it). It is passed the URL of the requested file and returns an Apache response code. It checks whether downloads are allowed and if so allows Apache to take its default action.

$response_code = $mp3->stream($file)

This method is called to stream an MP3 file. It is passed the URL of the requested file and returns an Apche response code. It checks whether streaming is allowed and then passes the request on to send_stream().

-item $mp3->send_playlist($urls,$shuffle)

This method generates a playlist that is sent to the browser. It is called from various places. $urls is an array reference containing the MP3 URLs to incorporate into the playlist, and $shuffle is a flag indicating that the order of the playlist should be randomized prior to sending it. No return value is returned.

$mp3_info = $mp3->find_mp3s($recurse)

This method searches for all MP3 files in the currently requested directory. $recurse, if true, causes the method to recurse through all subdirectories. The return value is a hashref in which the keys are the URLs of the found MP3s, and the values are hashrefs containing the MP3 tag fields recovered from the files ("title", etc.).

@urls = $mp3->sort_mp3s($mp3_info)

This method sorts the hashref of MP3 files returned from find_mp3s(), returning an array. The implementation of this method in Apache::MP3 sorts by physical file name only. Apache::MP3::Sorted has a more sophisticated implementation.

$response_code = $mp3->list_directory($dir)

This is the top level formatter for directory listings. It is passed the URL of a directory and returns an Apache response code.

$mp3->directory_top($dir)

This method lists the top part of the directory, including the title, the directory navigation list, and the big CD Icon in the upper left.

$mp3->generate_navpath_staircase($dir)

This method generates the list of parent directories, displaying them as links so that the user can navigate. It takes the URL of the current directory and returns no result.

$mp3->generate_navpath_arrows($dir)

This method does the same, except that the parent directories are displayed on a single line, separated by arrows.

$mp3->directory_bottom($dir)

This method generates the bottom part of the directory listing, including the module attribution and help information.

$mp3->subdir_list_top($directories)

This method generates the heading at the top of the list of subdirectories. $directories is an arrayref containing the subdirectories to list.

$mp3->subdir_list_bottom($directories)

This method generates the footer at the bottom of the list of subdirectories given by $directories. Currently it does nothing.

$mp3->subdir_list($directories)

This method invokes sort_subdirs() to sort the subdirectories given by $directories and displays them in a nicely-formatted table.

@directories = $mp3->sort_subdirs($directories)

This method sorts the subdirectories given in $directories and returns a sorted list (not an arrayref).

$html = $mp3->format_subdir($directory)

This method formats the indicated subdirectory by creating a fragment of HTML containing the little CD icon, the shuffle and stream links, and the subdirectory's name. It returns an HTML fragment used by subdir_list().

$mp3->get_help

This subroutine generates the "Quick Help Summary" link at the bottom of the page.

$mp3->list_subdirs($subdirectories)

This is the top-level subroutine for listing subdirectories (the part of the page in which the little CD icons appears). $subdirectories is an array reference containing the subdirectories to display

$mp3->list_mp3s($mp3s)

This is the top-level subroutine for listing MP3 files. $mp3s is a hashref in which the key is the path of the MP3 file and the value is a hashref containing MP3 tag info about it. This generates the buttons at the top of the table and then calls mp3_table_header() and mp3_list_bottom().

$mp3->mp3_table_header

This creates the first row (table headers) of the list of MP3 files.

$mp3->mp3_list_bottom($mp3s)

This method generates the buttons at the bottom of the MP3 file listing. $mp3s is a hashref containing information about each file.

$mp3->mp3_list($mp3s)

This routine sorts the MP3 files contained in $mp3s and invokes format_song() to format it for the table.

$arrayref = $mp3->format_song($song,$info,$count)

This method is called with three arguments. $song is the path to the MP3 file, $info is a hashref containing tag information from the song, and $count is an integer containing the song's position in the list (which currently is unusued). The method invokes format_song_controls() and format_song_fields() to generate a list of elements to be incorporated into cells of the table, and returns an array reference.

@array = $mp3->format_song_controls($song,$info,$count)

This method is called with the same arguments as format_song(). It returns a list (not an arrayref) containing the "control" elements of one row of the MP3 list. The control elements are all the doo-dads on the left-hand side of the display, including the music icon, the checkbox, and the [fetch] and [stream] links.

@array = format_song_fields($song,$info,$count)

This method is called with the same arguments as format_song(). It returns a list (not an arrayref) containing the rest of a row of the MP3 file display. This will include the title, artist, and so forth, depending on the values of the Fields configuration. variable.

($directories,$mp3s) = $mp3->read_directory($dir)

This method reads the directory in $dir, generating an arrayref containing the subdirectories and a hashref containing the MP3 files and their information, which are returned as a two-element list.

$hashref = $mp3->fetch_info($file)

This method fetches the MP3 information for $file and returns a hashref containing the MP3 tag information as well as some synthesized fields. The synthesized fields are track, which contains the same information as tracknum; description, which contains the title, album and artist merged together; and duration, which contains the duration of the song expressed as hours, minutes and seconds. Other fields are taken directly from the MP3 tag, but are downcased (for convenience to other routines).

@fields = $mp3->fields

Return the fields to display for each MP3 file. Reads the Fields configuration variable, or uses a default list.

$hashref = $mp3->read_cache($file)

Reads the cache for MP3 information about the indicated file. Returns a hashref of the same format used by fetch_info().

$boolean = $mp3->write_cache($file,$info)

Writes MP3 information to cache. $file and $info are the path to the file and its MP3 tag information, respectively. Returns a boolean indicating the success of the operation.

$result_code = $mp3->send_stream($file,$uri)

The send_stream() method generates an ICY (shoutcast) header for the indicated file (given by physical path $file and URI $uri) and streams it to the client. It returns an Apache result code indicating the success of the operation.

$boolean = $mp3->download_ok

Returns true if downloading files is allowed.

$boolean = $mp3->stream_ok

Returns true if streaming files is allowed.

$boolean = $mp3->check_stream_client

Returns true if the module should check the browser/MP3 player for whether it accepts streaming.

$boolean = $mp3->is_stream_client

Returns true if this MP3 player can accept streaming. Note that this is not a foolproof method because it checks a variety of non-standardized headers and user agent names!

$boolean = $mp3->read_mp3_info

Returns true if the module should read MP3 info (true by default).

$seconds = $mp3->stream_timeout

Returns the number of seconds after which streaming should time out. Used for "demo mode".

$lines = $mp3->file_list_is_long

Returns the number of lines in the MP3 file listing after which the list is considered to be "long". When a long list is encountered, the module places the control buttons at both the top and bottom of the MP3 file table, rather than at the bottom only. This method

$html = $mp3->home_label

Returns a fragment of HTML to use as the "Home" link in the list of parent directories.

$style = $mp3->path_style

Returns the style of the list of parent directories. Either "arrows" or "staircase".

$path = $mp3->cache_dir

Returns the directory for use in caching MP3 tag information

$int = $mp3->subdir_columns

Returns the number of columns to use in displaying subdirectories (little CD icons).

$dir = $mp3->default_dir

Returns the base directory used for resolving relative paths in the directories to follow.

miscellaneous directories and files

The following methods return the values of their corresponding configuration variables, resolved against the base directory, if need be:

stylesheet()   URI to the stylesheet file
parent_icon()	URI to the icon to use to move up in directory
                    hierarchy (no longer used)
cd_icon        URI for the big CD icon printed in the upper left corner
cd_list_icon   URI for the little CD icons in the subdirectory listing
song_icon	URI for the music note icons printed for each MP3 file
arrow_icon	URI for the arrow used in the navigation bar
help_url	URI of the document to display when user asks for help
$boolean = $mp3->skip_directory($dir)

This method is called during directory listings. It returns true if the directory should not be displayed. Currently it skips directories beginning with a dot and various source code management directories. You may subclass this to skip over other directories.

BUGS

Although it is pure Perl, this module relies on an unusual number of compiled modules. Perhaps for this reason, it appears to be sensitive to certain older versions of modules.

Can't find Apache::File at run time

David Wheeler <dwheeler@salon.com> has reported problems relating to Apache::File, in which the module fails to run, complaining that it can't find Apache::File in @INC. This affects Apache/1.3.12 mod_perl/1.24. Others have not yet reported this problem. This can be worked around by replacing all occurrences of Apache::File with IO::File.

Random segfaults in httpd children

Before upgrading to Apache/1.3.6 mod_perl/1.24, I would see random segfaults in the httpd children when using this module. This problem disappeared when I installed a newer mod_perl.

If you experience this problem, I have found that one workaround is to load the MP3::Info module at server startup time using the mod_perl perl.startup script made the problem go away. This is an excerpt from my perl.startup file:

# the !/usr/local/bin/perl
...
use Apache::Registry ();
use Apache::Constants();
use MP3::Info;
use Apache::MP3;
use CGI();
use CGI::Carp ();

Can't use -d $r->finfo

Versions of mod_perl prior to 1.22 crash when using the idiom -d $r->finfo (or any other idiom). Since there are many older versions still out there, I have replaced $r->finfo with $r->filename and marked their locations in comments. To get increased performance, change back to $r->finfo.

Misc

In the directory display, the alignment of subdirectory icon with the subdirectory title is a little bit off. I want to move the title a bit lower using some stylesheet magic. Can anyone help?

SEE ALSO

Apache::MP3::Sorted, MP3::Info, Apache

AUTHOR

Copyright 2000, Lincoln Stein <lstein@cshl.org>.

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.