#!/usr/bin/perl use strict; use Getopt::Std; use List::Util qw(shuffle); use File::Find; # TODO: # - Read song list from file # - Go backwars in playlist # - Seek inside of a song # This is the mapping we use to play a file # regex => 'command' # for the most simple case # regex => subref # for anything more complex my $map = { qr/\.ogg$/i => 'ogg123', qr/\.mp3$/i => 'mpg123-esd', qr/\.m4a$/i => 'mplayer', qr/^http:\/\//i => 'vlc', qr/\.(mod|xm|s3m|it)$/i => 'vlc -I curses' }; my %opt; getopts('ahosvz',\%opt); my @songs = @ARGV; # Check for songs given on STDIN unless(-t STDIN || @songs) { @songs = ; chomp @songs; } if(!@songs || $opt{h}) { print qq{ polly - call an appropriate player for the given audio media Usage: polly [-hzv] file1.mp3 file2.ogg *.mp3 music/ ... -a: announce the current song (festival) -h: show this -o: On Screen Display of song title (osdctl) -s: force sort (dirs are already sorted) -v: be verbose -z: shuffle the file list Also note that ^C during playback goes to next song, and two quick ^C's kill. If no songs are given, I'll try looking for them on STDIN. }; exit; } # If any song is a directory we should recurse my @songs_copy = @songs; foreach my $song (@songs_copy) { if(-d $song) { my @dirsongs; print "Adding all files in $song\n" if $opt{v}; find(sub { push @dirsongs, $File::Find::name }, $song); push @songs, sort @dirsongs; } } if($opt{s}) { print "Sorting list...\n" if $opt{v}; @songs = sort @songs; } if($opt{z}) { print "Shuffling list...\n" if $opt{v}; @songs = shuffle @songs; } print "Loaded " . (scalar @songs) . " songs.\n"; foreach my $song (@songs) { my ($type) = grep { $song =~ /$_/ } keys %$map; if($type) { my $cmd = $map->{$type}; print "$song\n"; if($opt{o}) { my $saysong = $song; $saysong =~ s/^.*\///; # Strip out directory $saysong =~ s/\....$//; # remove 3-letter extension $saysong =~ s/ - /,\n/; `notify-send 'Now playing:' '$saysong'`; } if($opt{a}) { my $saysong = $song; $saysong =~ s/.*\///; # Strip out directory $saysong =~ s/ - \d\d\d? - / - /; # Strip out track number $saysong =~ s/\....$//; # remove 3-letter extension $saysong =~ s/_+/ /g; # Turn separators into a pause (two newlines will work) $saysong =~ s/ - /\n\n/g; $saysong =~ s/&/ and /g; $saysong =~ s/'//g; #print "$saysong\n"; `say "$saysong"`; } my $extra = ''; $extra = '2&> /dev/null' unless $opt{v}; eval { local $SIG{INT} = sub { die "INTERRUPT"; }; if(ref $cmd) { $cmd->($song); } else { `$cmd "$song" $extra`; } }; if($@ =~ 'INTERRUPT') { print "\nGoing to next song...\n"; sleep 1; # Give them a chance to interrupt again } elsif($@) { die "Error: $@\n"; } } else { print "Didn't find type for $song\n" if $opt{v}; } }