#!/usr/bin/perl -w
use strict;
use CGI; # for escape
use Data::Dumper;
use HTTP::Response;
use POE qw(Component::Server::HTTP Component::MPG123);
$| = 1;
my $port = 8000; # HTTP server port
my $mp3dir = "/home/hossman/mp3/"; # where all your mp3s are
my @song_queue =
# initial queue of songs to play (if any)
(
# $mp3dir . 'a.mp3',
# $mp3dir . 'b.mp3',
);
##################################################################
# we'll fill this up.
my $global_status = { state => 'stoped',
total_frames => 0, total_time => 0,
last_frame => 0, last_time => 0,
};
##
#
# The MPG123 Component
#
# much of this was taken verbatim from the
# POE::Component::MPG123 test script
#
POE::Component::MPG123->spawn( alias => 'song_manager' );
POE::Session->create
( inline_states => {
_start => sub {
my ($kernel, $heap) = @_[KERNEL, HEAP];
$kernel->alias_set( 'song_manager' );
# Start the first song.
&play_next($kernel);
},
_stop => sub { },
# Receive status events from mpg123.
status => sub {
my ( $kernel, $heap,
$frames_played, $frames_left,
$time_played, $time_left
) = @_[KERNEL, HEAP, ARG0..ARG3];
$global_status->{state} = "playing";
unless ($global_status->{total_frames}) {
$global_status->{total_frames} = $frames_left;
$global_status->{total_time} = $time_left;
}
$global_status->{frame_count}++;
$global_status->{last_frame} = $frames_played;
$global_status->{last_time} = $time_played;
# Rather than wait for a specific frame and call
# it the end of the song, I'll set a brief delay
# here to time out if frames
# stop playing.
$kernel->delay( song_timeout => 0.25 );
},
song_info => sub {
my ($kernel, $heap, $info) = @_[KERNEL, HEAP, ARG0];
$global_status->{song_info} = $info;
},
file_info => sub {
my ($kernel, $heap, $info) = @_[KERNEL, HEAP, ARG0];
$global_status->{file_info} = $info;
},
# The pause succeeded.
song_paused => sub {
my ($kernel, $heap) = @_[KERNEL, HEAP];
$global_status->{state} = "paused";
},
# The song resumed after we asked it to.
song_resumed => sub {
my ($kernel, $heap) = @_[KERNEL, HEAP];
$global_status->{state} = "playing";
},
# The song stopped naturally.
song_stopped => sub {
my ($kernel, $heap) = @_[KERNEL, HEAP];
$global_status->{state} = "stoped";
$global_status->{file_info} = undef;
$global_status->{song_info} = undef;
$global_status->{total_time} = 0;
$global_status->{total_frames} = 0;
$global_status->{last_time} = 0;
$global_status->{last_frame} = 0;
&play_next($kernel);
},
# The player has quit.
player_quit => sub {
my ($kernel, $heap) = @_[KERNEL, HEAP];
$global_status->{state} = "player quit";
},
### Miscellaneous event handlers.
debug => sub { },
_child => sub { },
_signal => sub { 0 },
}
);
##
#
# The HTTP Component
#
# The docs for this component are a little lacking ...
# but this seems to work
#
my $httpd = new POE::Component::Server::HTTP
(
Port => 8000,
Headers => {
'Server' => 'My Server',
'Content-type' => 'text/html',
},
ContentHandler => {
'/pause/' => \&httpd_pause_handler,
'/req/' => \&httpd_req_handler,
'/list/' => \&httpd_list_handler,
'/next/' => \&httpd_next_handler,
'/' => \&httpd_status_handler,
},
);
#
# HTTPD Handlers
#
#
sub httpd_req_handler {
# the really cool one, we unshift a song on to the head
# of the list and queue it
my ($request, $response) = @_;
my $song = CGI::unescape($request->uri->query());
my $res = "";
if (-f $song) {
push @song_queue, $song;
$res = "REQUEST ACCEPTED: $song\n";
&play_next($poe_kernel);
} else {
$res = "what are you smoking? (no $song)\n";
}
$response->content
(&make_html_response($res, &get_status_info));
$response->code(RC_OK);
return RC_OK;
}
sub httpd_list_handler {
my ($request, $response) = @_;
my @songs = `find $mp3dir -follow -name \*.mp3`;
my $links = join ' ', map {
chomp;
"$_
" } @songs;
$response->code(RC_OK);
$response->content
(&make_html_response("Pick a song from the list below",
$links));
return RC_OK;
}
sub httpd_pause_handler {
my ($request, $response) = @_;
$poe_kernel->post( mpg123 => 'pause' );
$response->code(RC_OK);
$response->content
(&make_html_response("The current song has been (un)paused",
&get_status_info));
return RC_OK;
}
sub httpd_next_handler {
my ($request, $response) = @_;
$poe_kernel->post( mpg123 => 'stop');
$response->code(RC_OK);
$response->content
(&make_html_response("The last song was skipped",
&get_status_info));
return RC_OK;
}
sub httpd_status_handler {
my ($request, $response) = @_;
$response->code(RC_OK);
$response->content
(&make_html_response(&get_status_info));
return RC_OK;
}
sub make_html_response {
my ($r) = ('
'); return $r; } sub play_next { # takes a kernel, # plays if the state is "stoped" and there's a song waiting return unless scalar(@song_queue); return unless 'stoped' eq $global_status->{state}; $_[0]->post( mpg123 => play => shift(@song_queue) ); } sub get_status_info { my $r = "
";
$r .= "\nCurrent State: " . $global_status->{state} . "\n";
$r .= "Total Frames: " . $global_status->{total_frames} . "\n";
$r .= "Total Time: " . $global_status->{total_time} . "\n";
$r .= "Elapsed Frames: " . $global_status->{last_frame} . "\n";
$r .= "Elapsed Time: " . $global_status->{last_time} . "\n";
$r .= "\nSong...\n" . Dumper($global_status->{song_info});
$r .= "\nFile...\n" . Dumper($global_status->{file_info});
$r .= "\nQueue...\n" . join("\n", @song_queue);
$r .= "";
return $r
}
# Kick the kernel
$poe_kernel->run();
exit 0;