Apache-MP3
view release on metacpan or search on metacpan
sub aright { -align => shift->{lh}->right }
# align "right" (or, in case of Arabic (etc), really left).
sub aleft { -align => shift->{lh}->left }
# align "light" (or, in case of Arabic (etc), really right).
sub r { return shift->{r} }
sub html_content_type {
my $self = shift;
return 'text/html; charset=' . $self->lh->encoding
}
sub help_screen {
my $self = shift;
$self->r->content_type( $self->html_content_type );
return OK if $self->r->header_only;
print start_html(
-lang => $self->lh->language_tag,
-title => $self->x('Quick Help Summary'),
-dir => $self->lh->direction,
-head => meta({-http_equiv => 'Content-Type',
-content => $self->html_content_type
}
),
-script =>{-src=>$self->default_dir.'/functions.js'},
);
my $help_img_url = $self->help_img_url; # URL for the image
my ($url,$width,$height) = $help_img_url=~/(.+):(\d+)x(\d+)/;
$url ||= $help_img_url;
$width ||= 500;
$height ||= 400;
print img({-src => $url,
-alt => "",
-height => $height,
-width => $width,
$self->aleft,
}), "\n";
print join "\n".br(),
$self->help_figure_list
;
print "\n", end_html();
return;
}
sub help_figure_list {
my $self = shift;
# Provide a legend for the items in the figure
return(
b("A"). $self->x("= Stream all songs"),
b("B"). $self->x("= Shuffle-play all Songs"),
b("C"). $self->x("= Stream all songs"),
b("D"). $self->x("= Go to earlier directory"),
b("E"). $self->x("= Stream contents"),
b("F"). $self->x("= Enter directory"),
b("G"). $self->x("= Stream this song"),
b("H"). $self->x("= Select for streaming"),
b("I"). $self->x("= Download this song"),
b("J"). $self->x("= Stream this song"),
b("K"). $self->x("= Sort by field"),
);
}
sub run {
my $self = shift;
my $r = $self->r;
my(undef,$uribase) = fileparse($r->uri);
my(undef,$filebase) = fileparse($r->filename);
my $local = $self->playlocal_ok && $self->is_local;
my $base = $self->stream_base;
local $CGI::XHTML = 0;
# check that we aren't running under PerlSetupEnv Off
if ($ENV{MOD_PERL} && !$ENV{SCRIPT_FILENAME}) {
warn "CGI.pm cannot run with 'PerlSetupEnv Off', please set it to On";
}
# this is called to show a help screen
return $self->help_screen if param('help_screen');
# generate directory listing
return $self->process_directory($r->filename)
if -d $r->filename; # should be $r->finfo, but STILL problems with this
# simple download of file
return $self->download_file($r->filename) unless param;
# this is called to stream a file
if(param('stream')){
return $self->stream;
}
# this is called to generate a playlist on the current directory
return $self->send_playlist($self->find_mp3s)
if param('Play All');
# this is called to generate a playlist on the current directory
# and everything beneath
return $self->send_playlist($self->find_mp3s('recursive'))
if param('Play All Recursive') ;
# this is called to generate a shuffled playlist of current directory
return $self->send_playlist($self->find_mp3s,'shuffle')
if param('Shuffle');
# this is called to generate a shuffled playlist of current directory
return $self->send_playlist($self->find_mp3s,'shuffle')
if param('Shuffle All');
# this is called to generate a shuffled playlist of current directory
# and everything beneath
return $self->send_playlist($self->find_mp3s('recursive'),'shuffle')
if param('Shuffle All Recursive');
$uri =~ s!/?search/?!/!;
my $list = [map {"$uri/$_"} @files];
$self->shuffle($list);
$self->send_playlist($list);
return OK;
}
# otherwise don't know how to deal with this
$self->r->log_reason('Invalid parameters -- possible attempt to circumvent checks.');
return FORBIDDEN;
}
sub escape {
my $uri = CGI::escape(shift);
# unescape slashes so directories work right with mozilla
$uri =~ s!\%2F!/!gi;
return $uri;
}
# this generates the top-level directory listing
sub process_directory {
my $self = shift;
my $dir = shift;
return $self->list_directory($dir);
}
# this downloads the file
sub download_file {
my $self = shift;
my $file = shift;
my $type = $self->r->content_type;
my $is_audio = $self->supported_type ($self->r->content_type);
if ($is_audio && !$self->download_ok) {
$self->r->log_reason('File downloading is forbidden');
return FORBIDDEN;
} else {
return DECLINED; # allow Apache to do its standard thing
}
}
# stream the indicated file
sub stream {
my $self = shift;
my $r = $self->r;
return DECLINED unless -e $r->filename; # should be $r->finfo
unless ($self->stream_ok) {
$r->log_reason('AllowStream forbidden');
return FORBIDDEN;
}
if ($self->check_stream_client and !$self->is_stream_client) {
my $useragent = $r->headers_in->{'User-Agent'};
$r->log_reason("CheckStreamClient is true and $useragent is not a streaming client");
return FORBIDDEN;
}
my $mime = $r->content_type;
my $file = $r->filename;
my $url = $r->uri;
my $info = $self->fetch_info($file,$mime);
return DECLINED unless $info; # not a legit mp3 file?
my $fh = $self->open_file($file) || return DECLINED;
binmode($fh); # to prevent DOS text-mode foolishness
my $size = -s $file;
my $bitrate = $info->{bitrate};
if ($self->can('bitrate') && $self->bitrate) {
($bitrate = $self->bitrate) =~ s/ kbps//i;
# quick approximation
$size = int($size * ($bitrate / $info->{bitrate}));
}
my $description = $info->{description};
my $genre = $info->{genre} || $self->lh->maketext('unknown');
my $range = 0;
$r->headers_in->{"Range"}
and $r->headers_in->{"Range"} =~ m/bytes=(\d+)/
and $range = $1
and seek($fh,$range,0);
# Look for a descriptive file that has the same base as the mp3 file.
# Also look for various index files.
my $icyurl = $self->stream_base(1);
my $base = basename($file);
$base =~ s/\.\w+$//; # get rid of suffix
my $dirbase = dirname($file);
my $urlbase = dirname($url);
foreach ("$base.html","$base.htm","index.html","index.htm") {
my $file = "$dirbase/$_";
if (-r $file) {
$icyurl .= "$urlbase/$_";
last;
}
}
$r->assbackwards(1);
$r->connection->keepalive(1);
$r->connection->keepalives($r->connection->keepalives+1);
$r->print("ICY ". ($range ? 206 : 200) ." OK$CRLF");
$r->print("icy-notice1: <BR>This stream requires a shoutcast/icecast compatible player.<BR>$CRLF");
$r->print("icy-notice2: Apache::MP3<BR>$CRLF");
$r->print("icy-name: $description$CRLF");
$r->print("icy-genre: $genre$CRLF");
$r->print("icy-url:$icyurl$CRLF");
$r->print("icy-pub:1$CRLF");
$r->print("icy-br:$bitrate$CRLF");
$r->print("Accept-Ranges: bytes$CRLF");
$r->print("Content-Range: bytes $range-" . ($size-1) . "/$size$CRLF")
if $range;
$r->print("Content-Length: $size$CRLF");
$r->print("Content-Type: $mime$CRLF");
# print the HTML at the top of the page
sub page_top {
my $self = shift;
my $dir = shift;
my $title = $self->r->uri;
print start_html(
-title => $title,
-head => meta({-http_equiv => 'Content-Type',
-content => 'text/html; charset='
. $self->html_content_type
}),
-lang => $self->lh->language_tag,
-dir => $self->lh->direction,
-style => {-src=>$self->stylesheet},
-script =>{-src=>$self->default_dir.'/functions.js'},
);
}
# print the HTML at the top of a directory listing
sub directory_top {
my $self = shift;
my $dir = shift;
my $title = $self->r->uri;
my $links;
print start_table({-width => '100%'}), start_TR;
print start_td({-width=>'100%'});
if ($self->path_style eq 'staircase') {
$links = $self->generate_navpath_staircase($title);
} elsif ($self->path_style eq 'arrows') {
$links = $self->generate_navpath_arrows($title);
} elsif ($self->path_style eq 'slashes') {
$links = $self->generate_navpath_slashes($title);
}
print a({-href=>'./playlist.m3u?Play+All+Recursive=1'},
img({-src => $self->cd_icon($dir), $self->aleft, -alt=>
$self->x('Stream All'),
-border=>0})),
$links,
a({-href=>'./playlist.m3u?Shuffle+All+Recursive=1'},
font({-class=>'directory'}, '[',
$self->x('Shuffle All'),
']'
))
.' '.
a({-href=>'./playlist.m3u?Play+All+Recursive=1'},
font({-class=>'directory'}, '[',
$self->x('Stream All'),
']'
)),
br({-clear=>'ALL'}),;
if (my $t = $self->stream_timeout) {
print p(strong(
$self->x('Note:')
),' ',
$self->x("In this demo, streaming is limited to approximately [quant,_1,second,seconds].", $t),
"\n"
);
}
print end_td;
print end_TR, end_table;
}
# staircase style path
sub generate_navpath_staircase {
my $self = shift;
my $uri = shift;
my $home = $self->home_label;
my $indent = 3.0;
my @components = split '/',$uri;
unshift @components,'' unless @components;
my ($path,$links) = ('',br());
my $current_style = "line-height: 1.2; font-weight: bold; color: red;";
my $parent_style = "line-height: 1.2; font-weight: bold;";
for (my $c=0; $c < @components-1; $c++) {
$path .= escape($components[$c]) ."/";
my $idt = $c * $indent;
my $l = a({-href=>$path},$components[$c] || ($home.br({-clear=>'all'})));
$links .= div({-style=>"text-indent: ${idt}em; $parent_style"},
font({-size=>'+1'},$l))."\n";
}
my $idt = (@components-1) * $indent;
$links .= div({-style=>"text-indent: ${idt}em; $current_style"},
font({-size=>'+1'},$components[-1] || $home))."\n";
return $links;
}
# alternative display on one line using arrows
sub generate_navpath_slashes {
my $self = shift;
my $uri = shift;
my $home = $self->home_label;
my @components = split '/',$uri;
unshift @components,'' unless @components;
my $path;
my $links = br . ' ' ; #start_h1();
for (my $c=0; $c < @components-1; $c++) {
$links .= ' / ' if $path;
$path .= escape($components[$c]) . "/";
$links .= a({-href=>$path},font({-size=>'+1'},$components[$c] || $home));
}
$links .= ' / ' if $path;
$links .= font({-size=>'+1',-style=>'color: red'},($components[-1] || $home));
$links .= br;
return $links;
}
# alternative display on one line using arrows
sub generate_navpath_arrows {
my $self = shift;
my $uri = shift;
my $home = $self->home_label;
my @components = split '/',$uri;
foreach (@uris) {
# strip directory part
substr($_,0,length($dir)+1) = '' if index($_,$dir) == 0;
# turn into a URL
$_ = "$uri/$_";
}
return \@uris;
}
# recursive find
sub _find_mp3s {
my $self = shift;
my ($d,$recurse) = @_;
my ($directories,$files) = $self->read_directory($d);
# Add the directory back onto each file
unless ($d eq '.') {
foreach my $k (keys %$files) {
$files->{"$d/$k"} = $files->{$k};
delete $files->{$k};
}
}
if ($recurse) {
foreach (@$directories) {
my $f = $self->_find_mp3s("$d/$_",$recurse);
# Add the new files to our main hash
$files->{$_} = $f->{$_} foreach keys %$f;
}
}
return $files;
}
# sort MP3s
sub sort_mp3s {
my $self = shift;
my $files = shift;
return sort keys %$files;
}
#################################################
# interesting configuration directives start here
#################################################
#utility subroutine for configuration
sub get_dir {
my $self = shift;
my ($config,$default) = @_;
my $dir = $self->r->dir_config($config) || $default;
return $dir if $dir =~ m!^/!; # looks like a path
return $dir if $dir =~ m!^\w+://!; # looks like a URL
return $self->default_dir . '/' . $dir;
}
# return true if downloads are allowed from this directory
sub download_ok {
my $d = shift->r->dir_config('AllowDownload') || '';
return $d !~ /$NO/oi;
}
# return true if streaming is allowed from this directory
sub stream_ok {
my $d = shift->r->dir_config('AllowStream') || '';
return $d !~ /$NO/oi;
}
# return true if playing locally is allowed
sub playlocal_ok {
my $d = shift->r->dir_config('AllowPlayLocally') || '';
return $d =~ /$YES/oi;
}
# return true if we should check that the client can accomodate streaming
sub check_stream_client {
my $d = shift->r->dir_config('CheckStreamClient') || '';
return $d =~ /$YES/oi;
}
# return true if client can stream
sub is_stream_client {
my $r = shift->r;
my $h = $r->headers_in;
$h->{'Icy-MetaData'} # winamp/xmms
|| $h->{'Bandwidth'} # realplayer
|| $h->{'Accept'} =~ m!\baudio/mpeg\b! # mpg123 and others
|| $h->{'User-Agent'} =~ m!^NSPlayer/! # Microsoft media player
|| $h->{'User-Agent'} =~ m!^xmms/!;
}
# whether to read info for each MP3 file (might take a long time)
sub read_mp3_info {
my $d = shift->r->dir_config('ReadMP3Info') || '';
return $d !~ /$NO/oi;
}
# whether to time out streams
sub stream_timeout {
shift->r->dir_config('StreamTimeout') || 0;
}
# how long an album list is considered so long we should put buttons
# at the top as well as the bottom
sub file_list_is_long { shift->r->dir_config('LongList') || 10 }
sub home_label {
my $self = shift;
my $home = $self->r->dir_config('HomeLabel') ||
$self->x('Home');
return lc($home) eq 'hostname' ? $self->r->hostname : $home;
}
sub path_style { # style for the path to parent directories
lc(shift->r->dir_config('PathStyle')) || 'Staircase';
}
# where is our cache directory (if any)
sub cache_dir {
my $self = shift;
return unless my $dir = $self->r->dir_config('CacheDir');
my $rootdir = Apache2::ServerUtil::server_root();
return $dir if $dir =~ m!^/!;
return "$rootdir/$dir";
}
# columns to display
sub subdir_columns {shift->r->dir_config('SubdirColumns') || SUBDIRCOLUMNS }
sub playlist_columns {shift->r->dir_config('PlaylistColumns') || PLAYLISTCOLUMNS }
# various configuration variables
sub default_dir { shift->r->dir_config('BaseDir') || BASE_DIR }
sub stylesheet { shift->get_dir('Stylesheet', STYLESHEET) }
sub parent_icon { shift->get_dir('ParentIcon',PARENTICON) }
# 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>
# Or use the Apache::MP3::Playlist subclass to get persistent playlists
<Location /songs>
SetHandler perl-script
PerlHandler Apache::MP3::Playlist
</Location>
A B<demo version> can be browsed at http://www.modperl.com/Songs/.
=head1 DESCRIPTION
This module makes it possible to browse a directory hierarchy
containing MP3, Ogg Vorbis, or Wav 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.
NOTE: 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 I<Linking> for details.
=head2 Installation
This section describes the installation process.
=over 4
=item 1. Prequisites
This module requires mod_perl, MP3::Info (to stream MP3 files),
Ogg::Vorbis (to stream OggVorbis files), and Audio::Wav (for Wave
files) all of which are available on CPAN.
The module will automatically adjust for the absence of one or more of
the MP3::Info, Ogg::Vorbis or Audio::Wav modules by inhibiting the
display of the corresponding file type.
=item 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
AddType audio/playlist m3u M3U
AddType audio/x-scpls pls PLS
AddType application/x-ogg ogg OGG
AddType audio/wav wav WAV
Note that you need extemely large amounts of bandwidth to stream Wav
files, and that few audio file players currently support this type of
streaming. Wav file support is primarily intended to allow for
convenient downloads.
=item 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 F<apache_mp3> directory
from the Apache-MP3 distribution.
You may change the location of this directory by setting the
I<BaseDir> configuration variable. See the I<Customizing> section for
more details.
=item 4. Set Apache::MP3 to be the handler for the MP3 directory
In httpd.conf or access.conf, create a E<lt>LocationE<gt> or
E<lt>DirectoryE<gt> 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. A further elaboration is Apache::MP3::Playlist,
which uses cookies to manage a persistent playlist for the user.
=item 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. If you have a mod_perl
"startup" file, enter these lines:
use MP3::Info;
use Apache::MP3;
=item 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
hierarchically by artist and/or album name.
If you place a file named "cover.jpg" in any of the directories, that
image will be displayed at the top of the directory listing. You can
use this to display cover art.
If you place a list of .mp3 file names in a file with the .m3u
extension, it will be treated as a playlist and displayed to the user
with a distinctive icon. Selecting the playlist icon will download
the playlist and stream its contents. The playlist must contain
relative file names, but may refer to subdirectories, as in this
example:
# file: folk_favorites.m3u
Never_a_Moment_s_Thought_v2.mp3
CacheDir path -none-
HelpImgURL URL apache_mp3_fig1.gif:374x292
StreamBase URL -none-
LocalNet subnet -none-
DISPLAY OPTIONS
ArrowIcon URL right_arrow.gif
CoverImage filename cover.jpg
CoverImageSmall filename cover_small.jpg
PlaylistImage filename playlist.jpg
DescriptionFormat string -see below-
DirectoryIcon URL cd_icon_small.gif
PlaylistIcon URL playlist.gif
Fields list title,artist,duration,bitrate
HomeLabel string "Home" (or translation)
LongList integer 10
MissingComment string "unknown" (or translation)
PathStyle Staircase|Arrows|Slashes Staircase
SongIcon URL sound.gif
SubdirColumns integer 1
Stylesheet URL apache_mp3.css
TitleIcon URL cd_icon.gif
DefaultLanguage languagetag en-US
=head2 General Configuration Variables
=over 4
=item AllowDownload I<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 (but not
impossible) for users to download the MP3s.
The module recognizes the arguments "yes", "no", "true" and "false".
The default is "yes".
Note that this setting only affects MP3 files. Other files, including
cover art and playlists, can still be downloaded.
=item AllowStream I<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."
=item AllowPlayLocally I<yes|no>
If you set AllowPlayLocally to "yes", then the playlists generated by
the module will point to the physical files when handling requests
from a user that happens to be working on the same machine. This is
more efficient, and allows the user to pause playback, fast forward,
and so on. Otherwise, the module will treat local users and remote
users the same. The default is "no".
=item CheckStreamClient I<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".
=item ReadMP3Info I<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 I<filename> and I<description>, which will both be
set to the physical filename of the MP3 file.
=item StreamTimeout I<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 uses the average bitrate of the song, it may be
off by a second or two when streaming variable bitrate MP3s.
=back
=head2 Configuration Variables Affecting Paths and Directories
=over 4
=item BaseDir I<URL>
The B<BaseDir> variable sets the URL in which Apache::MP3 will look
for its icons and stylesheet. You may use any absolute local or
remote URL. Relative URLs are not accepted.
The default is "/apache_mp3."
=item CacheDir I<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. If not specified, Apache::MP3 will
not cache the file information, resulting in slower performance on
large directories.
=item HelpImgURL I<URL:widthxheight>
The URL of the image that's inlined on the page that appears
when the user presses the "Quick Help
Summary" link at the bottom of the page. You can declare the
size of this image
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
Note: I prepared this image on an airplane, so it isn't as clean as I
would like. Volunteers to make a better help page are welcomed!
=item StreamBase I<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 when running behind a firewall and the web server can't figure out
the correct address for the playlist automatically.
Example:
If the song requested is http://www.foobar.com/Songs/Madonna_live.m3u?stream=1
and B<StreamBase> is set to I<http://streamer.myhost.net>, then the URL
placed in the playlist will be
http://streamer.myhost.net/Songs/Madonna_live.m3u?stream=1
The path part of the URL is simply appended to StreamBase. If you
want to do more sophisticated URL processing, use I<mod_rewrite> or
equivalent.
=item LocalNet I<URL>
This configuration variable is used in conjunction with B<StreamBase>
to disable B<StreamBase> for clients on the local network. This is
needed for firewall configurations in which the web server is accessed
by one address & port by hosts behind the firewall, and by another
address & port by hosts outside the firewall.
The argument is a dotted subnet address, or a space-delimited list of
subnets. For example:
PerlSetVar LocalNet "192.168.1 192.168.2 127.0.0.1"
Address matching is done by matching the address from left to right,
with an implied dot added to the end of the subnet address. More
complex subnet matching using netmasks is desirable, but not
implemented.
=back
=head2 Configuration Variables Affecting the Visual Display
=over 4
=item ArrowIcon I<URL>
Set the icon used for the arrows displayed between the components of
the directory path at the top of the directory listing.
=item CoverImage I<filename>
Before displaying a directory, Apache::MP3 will look inside the
directory for an image file. This feature allows you to display
digitized album covers or other customized icons. The default is
"cover.jpg", but the image file name can be changed with
I<CoverImage>. If the file does not exist, the image specified by
I<TitleIcon> will be displayed instead.
=item CoverImageSmall I<filename>
Before displaying the list of subdirectories, Apache::MP3 will check
inside of each for an image file of this name. If one is present, the
image will displayed rather than the generic I<DirectoryIcon>. The
default is "cover_small.jpg".
=item DescriptionFormat I<string>
The "Description" field, which is used both in the Description column
of the directory index and in the metadata sent to the player during
streaming, has a default format of I<title>-I<artist>-I<album>. The
description is constructed in such a way that the hyphen is omitted if
the corresponding field of the song's MP3 tag is empty.
You can customize this behavior by providing a I<DescriptionFormat>
string. These strings combine constant characters with %x format
codes in much the way that sprintf() does. For example, the directive
shown below will create descriptions similar to I<[Madonna] Like a
Virgin (1980)>.
PerlSetVar DescriptionFormat "[%a] %t (%y)"
The full list of format codes follows:
Table 2: I<DescriptionFormat> Field Codes
Code Description
---- -----------
%a Artist name
%c Comment
%d Duration, in format 00:00 (like 15:20 for 15 mins 20 sec)
%f Name of physical file (minus path)
%g Genre
%l Album name
%m Minutes portion of duration, usually used with %s
%n Track number
%q Sample rate, in kHz
%r Bitrate, in kbps
%s Seconds portion of duration, usually used with %m
%S Duration, expressed as total seconds
%t Title
%y Year
=item DirectoryIcon I<URL>
Set the icon displayed next to subdirectories in directory listings,
"cd_icon_small.gif" by default. This can be overridden on a
directory-by-directory basis by placing a I<CoverImageSmall> image
into the directory that you want to customize.
=item PlaylistIcon I<URL>
Set the icon displayed next to playlists in the playlist listings,
"playlist.gif" by default. You can change this icon on a
directory-by-directory basis by placing a file with this name in the
current directory.
=item PlaylistImage I<filename>
Before displaying a playlist, the module will check inside the current
directory for an image file named "playlist.jpg" to use as its icon.
This directive changes the name of the playlist image file. If no
image is found, the icon specified by I<PlaylistIcon> is used instead.
=item Fields I<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.
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
=head2 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 C<@ISA> package global or,
in Perl 5.6 and higher, with the C<use base> directive. Your module
can then override existing methods and define new ones.
This module uses the I<mod_perl> method invocation syntax for handler
invocation. Because of this, if you override the handler() method, be
sure to give it a prototype of ($$). If you override new(), be sure
to place the Apache::Request object in an instance variable named 'r'.
See the MP3.pm module for details.
One implication of using the method invocation syntax is that the
Apache::MP3 object is created at server configuration time. This
means that you cannot tweak the code and simply restart the server,
but must formally stop and relaunch the server every time you change
the code or install a new version. This disadvantage is balanced by a
savings in memory consumption and performance.
See I<The Apache::MP3 API> below for more information on overriding
Apache::MP3 methods.
=head1 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:
=over 4
=item 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>
=item 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>
=item 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.
=item 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.
=item 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.
=item 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.
=item 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.
=item 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>
=back
=head1 The Apache::MP3 API
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.
=item @array = $mp3->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.
=item ($directories,$mp3s) = $mp3->read_directory($dir)
This method reads the directory in C<$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.
=item $hashref = $mp3->fetch_info($file)
This method fetches the MP3 information for C<$file> and returns a
hashref containing the MP3 tag information as well as some synthesized
fields. The synthesized fields are I<track>, which contains the same
information as I<tracknum>; I<description>, which contains the title,
album and artist merged together; and I<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).
=item Apache::MP3->path_escape($scalarref)
This is a limited form of CGI::escape which does B<not> escape the
slash symbol ("/"). This allows URIs that correspond to directories
to be escaped safely. The escape is done inplace on the passed scalar
reference.
=item @fields = $mp3->fields
Return the fields to display for each MP3 file. Reads the I<Fields>
configuration variable, or uses a default list.
=item $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().
=item $boolean = $mp3->write_cache($file,$info)
Writes MP3 information to cache. C<$file> and C<$info> are the path
to the file and its MP3 tag information, respectively. Returns a
boolean indicating the success of the operation.
=item $boolean = $mp3->download_ok
Returns true if downloading files is allowed.
=item $boolean = $mp3->stream_ok
Returns true if streaming files is allowed.
=item $boolean = $mp3->check_stream_client
Returns true if the module should check the browser/MP3 player for
whether it accepts streaming.
=item $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!
=item $boolean = $mp3->read_mp3_info
Returns true if the module should read MP3 info (true by default).
=item $seconds = $mp3->stream_timeout
Returns the number of seconds after which streaming should time out.
Used for "demo mode".
=item $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
=item $html = $mp3->home_label
Returns a fragment of HTML to use as the "Home" link in the list of
parent directories.
=item $style = $mp3->path_style
Returns the style of the list of parent directories. Either "arrows"
or "staircase".
=item $path = $mp3->cache_dir
Returns the directory for use in caching MP3 tag information
=item $int = $mp3->subdir_columns
Returns the number of columns to use in displaying subdirectories
(little CD icons). If the return value is 1, directory access-time
and modification-time are displayed. If not equal to one, these
data are suppressed.
=item $dir = $mp3->default_dir
Returns the base directory used for resolving relative paths in the
directories to follow.
=item 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
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
The following methods return the values of their corresponding
configuration variables, resolved against the current directory, but if
that fails, against the base directory. This is useful for customizing
the appearance icons on a per-directory basis. For example, I like my
directories containing shoutcast playlists to appear differently than
my directories containing mp3 and m3u files.
cd_list_icon URI for the little CD icons in the subdirectory listing
playlist_icon URI for the playlist icon
=item $boolean = $mp3->skip_directory($dir)
( run in 1.309 second using v1.01-cache-2.11-cpan-140bd7fdf52 )