#!/usr/bin/perl #use Xmms::Remote; use Getopt::Std; # Playsong # $Id: playsong 109 2004-03-11 17:45:47Z steve $ # # $Log$ # Revision 1.6 2004/03/11 17:45:47 steve # uses mplayer now and should have the remote option turned on # # Revision 1.5 2003/01/25 19:43:24 steve # * doesn't search for m3u files anymore unless explicitly put on the command line. # * handles system calls more gracefully # # Revision 1.4 2002/10/30 09:14:06 steve # typo # # Revision 1.3 2002/10/30 09:09:36 steve # now handles Xmms::Remote correctly - it ignores it if you don't have it # plays mods and other stuff using mikmod and playmidi # # Revision 1.2 2002/10/30 07:55:55 steve # now handles searching Properly by pulling in tons of results from locate and # filtering internally. Minor other cleanups as well as the moving of -f to -x # which just makes more sense. # # Revision 1.1 2002/02/18 07:55:58 steve # Initial revision # printHelp() if scalar @ARGV == 0; my %argparam; my $search = ""; my $locate = "locate"; my $mpg123 = "mpg321 -o alsa09"; my $ogg123 = "ogg123 -d alsa"; my $mikmod = "mikmod"; my $mplayer = "mplayer"; my $mplayer_quiet = "-really-quiet"; getopts("N:Idhrepoilqkx:c:"); my $session = 0; $session = $opt_N if $opt_N ne ""; my $pl_clear = $opt_p || $opt_l; my $no_play = $opt_l; my $enqueue = $opt_e; my $shuffle = !($opt_o); my $insert = $opt_i; my $dedupe = $opt_d; printHelp() if $opt_h; $enqueue = 1 if $pl_clear; $enqueue = 1 if $insert; $dedupe = 0 unless $shuffle; # dedupe's algorythm currently messes with # the order... this sucks for playlists my $quiet = ""; $quiet = "-q" if $opt_q; my $csensative = "-i"; $csensative = "" if $opt_I; my $regexp = ""; $regexp = " -r" if $opt_r; srand(); # playlist extentions my @m3u_ext = qw( m3u ); my $m3ure = join "|", @m3u_ext; # these are the file types that are supported by the script. Any search # results with any one of these extentions will be enqueued / played. my %fTypes = ( # "ogg" => [qw( ogg )], # "mp3" => [qw( mp3 mp2 )], "mplayer" => [qw( ogg mp3 mp2 )], "mod" => [qw( mod xm s3m xm\.zip mod\.zip it )], "midi" => [qw( mid midi )] ); my @fTypes = (); my %ext_table; foreach my $type ( keys %fTypes ){ push @fTypes, @{$fTypes{$type}}; # this inverts the hash for quicker lookups elsewhere foreach my $ext ( @{$fTypes{$type}} ){ $ext_table{ $ext } = $type; } } my $types = join "|", @fTypes; my @playlist; my $pwd=`pwd`; chop $pwd; my $song; my $songs; my $searchwild; # check to see if there's the XMMS::Remote module and use it if it exists eval { require Xmms::Remote; }; my $has_xmms = $@ eq ""; my $remote; $remote = Xmms::Remote->new($session) if $has_xmms; ########################################################################## if($regexp){ $searchpre = ''; $searchpost = '.*\.mp[[:digit:]]$'; }else{ $searchpre = '"*'; $searchpost = '*"'; } foreach $songs (@ARGV){ foreach $song (split(/\n/, $songs)){ unless($song =~ /(\.(:?$types)\s*|^\-|^http.+|$m3ure)$/i){ open SRCH, "$locate $csensative $regexp $searchpre$song$searchpost|"; my @searchlist = ; close SRCH; chomp @searchlist; if(! @searchlist ){ next } # append.. @playlist = (@playlist, @searchlist); # filter out anything that's not of the appropriate file extensions @playlist = grep /\.(:?$types)$/i, @playlist; }else{ if($song=~/^http:\//i){ # if it's an address push(@playlist, $song); } elsif($song=~/^\-$/){ print STDERR "processing pipe\n"; my $wd = $pwd; while(){ chop; $_ = findfull($_,$wd); push(@playlist, $_); print "$_\n"; } } elsif(-T $song){ # if it's a playlist $song = findfull($song, $pwd); $song =~/^(.+)\//; my $wd = $1 if defined $1; open PLOP, "$song"; while(){ chop; $_ = findfull($_,$wd); push(@playlist, $_); } close PLOP; }else{ unless($song=~/^\//){$song = "$pwd/$song"} push(@playlist, $song); } } } } @playlist = dedupe(@playlist) if $dedupe; # filter out the filter string @playlist = grep(!/$opt_x/i, @playlist) if $opt_x ne ""; if($shuffle){ @playlist = shuffle(@playlist); } if($opt_c){ $opt_c=~/(\d+)/; unless(defined $1){ die "Error: Invalid argument for flag -c\n" } @playlist = @playlist[0..$1-1]; } @playlist = grep(/^http:/i || -e $_, @playlist) unless $opt_k; if( $has_xmms && $enqueue ){ if($pl_clear){$remote->playlist_clear} my @chopped; if($insert){ my @oldplaylist = @{$remote->get_playlist_files}; my $length = $remote->get_playlist_length; for (my $pos = $length; $pos > $remote->get_playlist_pos; $pos--){ push @chopped, pop @oldplaylist; $remote->playlist_delete($pos); } } foreach $item (@playlist){print "$item\n"} #$remote->playlist_add(\@playlist); my @tmplist = @playlist; my $maxEnqueue = 500; do{ my $enq = $maxEnqueue; $enq = $#tmplist if $#tmplist < $maxEnqueue; my @playenq = @tmplist[0..$enq]; #$enq = (join("\n", @playenq)); $remote->playlist_add(\@playenq) if scalar @tmplist > 0; @tmplist = @tmplist[($enq + 1) .. scalar(@tmplist)]; }while($#tmplist > 0); if($insert){remote->playlist_add(\@chopped)} if($pl_clear && !$no_play && scalar(@playlist) > 0){$remote->play} exit; } foreach (@playlist){print "$_\n"} print "---------------------------------------------\n" if @playlist; my $prev_fmt = ""; my $fmt = ""; my @list_part; my $list_count = 0; # this is needed as you need to divide the task of playing # to the various players. foreach my $song (@playlist){ push @list_part, $song; # pattern-match file extension to the "type" my $ext = ""; if( $song =~ /.+\.(.+?)$/ ){ $ext = $1; } $fmt = $ext_table{ lc $ext }; my $next; my $play = 0; # if it's a different format if($prev_fmt ne "" && $prev_fmt ne $fmt) { $play = 1; $next = pop @list_part; } # or if it's the end of the playing. $play = 1 if $list_count == $#playlist; # play the extracted bit of the playlist if( $play ){ # gotta check something. $prev_fmt = $fmt unless $prev_fmt; if( $prev_fmt eq "ogg" ){ (system( $ogg123, @list_part) == 0 ) || die "\n"; @list_part = ($next); }elsif( $prev_fmt eq "mp3" ){ open MPG123, "|$mpg123 $quiet -@ -"; print MPG123 join("\n", @list_part); close MPG123; @list_part = ($next); }elsif( $prev_fmt eq "mplayer" ){ (system( $mplayer, ($quiet ? $mplayer_quiet : ""), "-quiet", @list_part) == 0 ) || die "\n"; @list_part = ($next); }elsif( $prev_fmt eq "mod" ){ (system( $mikmod,"-q", @list_part) == 0 ) || die "\n"; @list_part = ($next); }elsif( $prev_fmt eq "midi" ){ (system( "playmidi", @list_part) == 0 ) || die "\n"; @list_part = ($next); } } $list_count++; $prev_fmt = $fmt; } sub shuffle { # @playlist my @playlist = @_; my $numb = 0; my $temp = 0; for(my $a = 0; $a < scalar(@playlist); $a++){ $numb = int(rand()*scalar(@playlist)); $temp = $playlist[$a]; $playlist[$a] = $playlist[$numb]; $playlist[$numb] = $temp; } return @playlist; } # deprecated sub dedupe{ # kill dupe files my @playlist = @_; my %chk; my $remote = "fire"; my @playout; foreach my $song (@playlist){ $song=~/.*\/(.*?\/.+?)$/; if(!(exists $chk{$1}) || !($song=~/$remote/)){ $chk{$1} = $song; } } @playlist = sort values %chk; return @playlist; } sub findfull{ # filename, working dir my ($file, $wd) = @_; unless($file=~/^\// || $file=~/^http:\//i || $file=~ /^\s*$/){$file = "$wd/$file"} return $file; } sub printHelp{ # prints some help for cmd line args die "usage: playsong [options] [search or local filename] options: -N n the xmms session -e search enqueue the song in the xmms playlist -p search wipe the xmms playlist, enqueues and starts playing -l search wipe the xmms playlist and enqueue the search -i search inserts a song in the xmms playlist (broken) -c n Count: plays only n songs -o Ordered: plays the songs in order - doesn't shuffle -d doesn't de-deupe -x regexp eXcludes matches of 'regexp' from the list -k disable file existance checking -q makes the output relatively quiet -I make playsong search case-sensatively -r bare search queries are treated as POSIX regular expressions -h displays this text 'search' can be a text string, a filename or any combination of the two. if it is a text string, it searches through the locate database and plays the songs that match the text string in random order. ex: playsong -p R.E.M \"/mp3/techno/Orbital/The Box.mp3\" ambient.m3u Moby/Play "; }