#!/usr/local/bin/perl

# whisper_1_0.pl
# Scott Parks	code@levitator.org
# 01/26/00
# streaming mp3 server. Works with Winamp client. If you hack this script, give
# due credit. This is a free distribution of my time and effort.

use Socket;
use Getopt::Std;

my $VERSION = 1.0;

getopt('plqs',\%args);
&help() if(!defined $args{l});
$port = 2020 ;
$port=$args{p} if(defined $args{p});
&help() if($port < 1 || $port > 65535);

$quiet=0; $quiet=1 if(exists $args{q});
$loop=1; $loop=0 if(exists $args{s});

($name, $aliases, $protocol) = getprotobyname('tcp');
($name, $aliases, $port) = getservbyport($port, 'tcp') if ($port !~ /^\d+$/);

print "Listening on port $port...\n"  if(!$quiet);

socket(S,AF_INET,SOCK_STREAM,$protocol) || die "socket : $!";
($err |= setsockopt(S, SOL_SOCKET, SO_REUSEADDR, 1)) or die "Can't set sock option\n"; 

$sockaddr = 'S n a4 x8';
$this = pack($sockaddr, AF_INET, $port, "\0\0\0\0");
bind(S, $this) || die "bind : $!";

listen(S,10) || die "listen: $!";
select(S); $| = 1; select(STDOUT);

for ($con = 1; ; $con++) {
   printf("Waiting for connection %d....\n", $con)  if(!$quiet);
   ($addr = accept(NS,S)) || die $!;

   select(NS); $| = 1; select(STDOUT);

   if (($child = fork()) == 0) {
      %songs=();
      &make_list($args{l});
      &serv_client();   
   }
   close(NS);
} 

#### subs start here  ##########


sub clean_str() {
   local $str=@_[0];

   $str=~tr/\r|\n//d;
   $str=~tr/\t/ /;
   $str=~tr/ //s;
   return $str;
}

sub start_stream() {
   local $agent=@_[0], $ret="";

   local $buff="HTTP/1.1 200 OK\n\n";
   print NS $buff;
}

sub serv_client() {
   ($af,$port, $inetaddr) = unpack($sockaddr, $addr);
   @inetaddr = unpack('C4', $inetaddr);
   print "Connect: $con @inetaddr:$port\n" if(!$quiet);

   local $byts=0,$agent="",$buf="",$lin="";
   while (<NS>) {
      $buf=$_;
      $lin = &clean_str($buf);
      $agent="winamp" if($lin=~/winamp/gi);
      next if(length($lin));

      &start_stream($agent);
      if($loop) {
         local $cnt=1;
         do { 
            print "Pass: $cnt\n" if(!$quiet); 
            $cnt++;
         } while(&stream_data());
      } else {
         print "Single pass through $args{l}\n" if(!$quiet); 
         &stream_data();
      }
      print "Playlist done.\n" if(!$quiet);
      close(NS);
   }
   print "Client be gone .\n" if(!$quiet);
   close(NS);
   exit;
}

sub make_list() {
   local $playlist=@_[0], $fil, $idx=0;
   local @fils=qx{cat $playlist};

   foreach $fil(@fils) {
      $fil=~tr/\n|\r//d;
      $songs{$idx++}="$fil"; 
   } 
}

sub stream_data() {
   local $sze=4096, $byts=0, $buf="", $song, $idx;

   foreach $idx(sort {$a <=> $b} keys %songs) {
      $song=$songs{$idx};
      open(FDS,"<$song") or return 0;
      print "Begin Streaming of -$song-\n" if(!$quiet); 
      $byts=$sze;
      while(($byts=read(FDS, $buf, $sze))) {
         print NS $buf if(length($buf) > 2) or return 0;
      }
      print "End Streaming of -$song-\n" if(!$quiet); 
      sleep 3;
   }
   return 1;
 }

sub help() {
   print "Usage: $0 -l -p -q -s\n";
   print "\t-l playlist, file containing full path to each .mp3 file. Mandatory\n";
   print "\t-p port number. Default is 2020\n";
   print "\t-q Quiet mode. No informative messages.\n";
   print "\t-s Single pass through playlist. Default is constant loop\n";
   print "\n\nExample:\n";
   print "\t$0 -l ./playlist\n";
   print "\tUsing default port (2020). Use your WinAmp player and 'Play Location'\n";
   print "\thost.domain.com:2020 and it will magically start streaming.\n\n";

   exit;
}

=head1 NAME
whisper_1_0.pl

=head1 DESCRIPTION
MP3 Server that works with the WinAmp client

=head1 README

This is free shitware. It works great with the winamp client.
It'll run on any UNIX system with PERL. No special CPAN mods
required. If you hack this code and pass it on, give credit 
where credit is due. Time and effort was put into creating
this; albeit, not much. 

code@levitator.org
Scott Parks 01/26/00


whisper_1_0.pl - How to run it.

1. Use the WinAMP Client. That's what it's designed
   around.

2. Create a playlist
   This is a file with a complete path to a mp3 file.
   A playlist might look like this:

   /export/mp3/song1.mp3
   /export/mp3/song2.mp3
   /export/mp3/song3.mp3

3. Ensure the first line of whisper_1_0.pl points to your
   perl binary:

   #!/usr/local/bin/perl 

4. Run the script:
   
   ./whisper_1_0.pl -l playlist

5. Start your winamp client and 'Play Location' 
   http://your.server.dom:2020 

That'll get you running.

Options Summary
Run the program without any parameters to get the help 
message. Here's a summary of each option:
  
   -l the playlist file
   -p port to listen on. The default is 2020. Remeber, 
      you must use a port number above 1023 if you
      don't have root.
   -s Single pass through the playlist file. With this
      switch each song in the playlist will be played 
      once and the client will be dropped. The default
      is to continuosly loop through the playlist until
      the client bails or the server stops.

   -q Quiet mode. Turn off information messages.


That would be just about it.


=head1 PREQUISITES
PERL 5.004 or greater
Any Unix OS or more precisely, anything that has a fork system call
and runs PERL

=pod SCRIPT CATEGORIES
Audio/MP3

=cut



