#!/usr/local/bin/perl # whisper_1_1.pl # Scott Parks code@levitator.org # 01/26/00 # Modified: 09/28/00 # streaming mp3 server. Works with Winamp client. use Socket; use Getopt::Std; my $VERSION = 1.1; getopt('plqsrd',\%args); &help() if(!defined $args{l} && !defined $args{d}); $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(); &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 () { $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 $fil, $idx=0; if(defined($args{l})) { local $playlist=$args{l}; local @fils=qx{cat $playlist}; foreach $fil(@fils) { $fil=~tr/\n|\r//d; @songs[$idx++]="$fil"; } } elsif (defined($args{d})) { local $dir=$args{d}; local @fils=qx{ls $dir | grep .mp3}; foreach $fil(@fils) { $fil=~tr/\n|\r//d; @songs[$idx++]="$dir\/$fil" if(length($fil) && length($dir)); } } &shuffle(\@songs) if(exists($args{r})); } sub stream_data() { local $sze=4096, $byts=0, $buf="", $song, $idx; foreach $song(@songs) { if(!open(FDS,"<$song")) { print "NOT FOUND, is path correct\?: $song\n"; sleep(3); next; } 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 shuffle() { local $i, $j, $array = shift; srand(time()); for($i = @$array; --$i; ) { $j = int rand($i+1); next if $i == $j; @$array[$i,$j] = @$array[$j,$i]; } } sub help() { print "Usage: $0 [-l | -d] -p -q -s\n"; print "\tNote: -l or -d must be specified\n"; print "\t-l playlist, file containing full path to each .mp3 file. \n"; print "\t-d full path to directory containing mp3 files\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 "\t-r Shuffle the playlist, don't play in listed order,\n\t make sure it's the last switch on the command line.\n"; print "\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_1.pl =head1 DESCRIPTION MP3 Server that works with the WinAmp client =head1 README whisper_1_1.pl - MP3 streaming server used with WinAmp client. It'll run on any UNIX system with PERL. No special CPAN mods required. WinAmp can be obtained at www.winamp.com code@levitator.org Scott Parks 01/26/00 whisper_1_1.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_1.pl points to your perl binary: #!/usr/local/bin/perl 4. Run the script: ./whisper_1_1.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 -d directory to look in for mp3's, non-recursive -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. -r shuffle your playlist, be it a directory or file 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