/utunes.pl
#!/usr/bin/perl
use strict;
use POSIX qw(:signal_h);

my $player = 'mplayer';
my $uthome = $ENV{HOME} . '/.utunes';

# First, make sure we're set up
if (! -e $uthome) {
	mkdir $uthome;
}

$SIG{CHLD} = sub { wait };
my $playpid = 0;
sub play {
	my $file = shift;
	my $finish = 1;
	$SIG{USR2} = sub {
		kill SIGTERM, $playpid;
		$finish = 0;
	};
	$playpid = fork();
	if ($playpid) {
		my $r = 0;
		while ($r != $playpid) {
			$r = waitpid $playpid, 0;
			print STDERR "waitpid returned $r";
		}
		$playpid = 0;
	} else {
		print $file;
		exec $player,$file;
		die "Could not exec $player";
	}
	return $finish;
}

sub daemon {
	if (-e "$uthome/pid") {
		open PID, "$uthome/pid";
		my $pid = <PID>; chomp $pid;
		close PID;
		print "Daemon already running on pid $pid\n";
		return;
	}
	my $pid = fork();
	if ($pid) {
		open PID, ">$uthome/pid";
		print PID "$pid\n";
		close PID;
		return;
	}
	open STDIN, '/dev/null';
	open STDOUT, '>/dev/null';
	#open STDOUT, ">>$uthome/error.log";
	open STDERR, ">>$uthome/error.log";
	my @list = ();
	my $c;
	sub reload {
		if (-e "$uthome/current") {
			open CURRENT, "$uthome/current";
			$_ = <CURRENT> or $_ = '0';
			close CURRENT;
			/^(\d+)/;
			$c = $1;
		} else {
			$c = 0;
		}
		if (-e "$uthome/playlist") {
			@list = ();
			open PLAYLIST, "$uthome/playlist";
			while (<PLAYLIST>) {
				chomp;
				push(@list, $_);
			}
			close PLAYLIST;
		}
	}
	reload();
	$SIG{USR1} = \&reload;
	sub ohnoes {
		kill SIGTERM, $playpid if $playpid;
		unlink "$uthome/pid";
		exit 0;
	}
	$SIG{TERM} = \&ohnoes;
	while ($c <= $#list) {
		open CURRENT, ">$uthome/current";
		print CURRENT "$c $list[$c]\n";
		close CURRENT;
		if (play($list[$c])) {
			$c++;
		}
	}
	unlink "$uthome/current";
	ohnoes();
}

sub fullpath {
	my $path = shift;
	return $path if m'^/';
	my $pwd;
	chomp ($pwd = `pwd`);
	return "$pwd/$path";
}

my $cmd = shift;
my $pid;
if (-e "$uthome/pid") {
	open PID, "$uthome/pid";
	$pid = <PID>; chomp $pid;
	close PID;
}
my $current;
if (-e "$uthome/current") {
	open CURRENT, "$uthome/current";
	$current = <CURRENT>; chomp $current;
	close CURRENT;
}

sub updatelist {
	return unless $pid;
	kill SIGUSR1, $pid;
}

sub hurk {
	return unless $pid;
	kill SIGUSR2, $pid;
}

if (not defined $cmd) {
	if (-e "$uthome/current") {
		$current =~ s/^\d+ //;
		print "$current\n";
	} else {
		print "No song currently playing.\n";
	}
} elsif ($cmd eq 'add' or $cmd eq 'load') {
	if ($cmd eq 'add') {
		open PLAYLIST, ">>$uthome/playlist";
	} else {
		unlink "$uthome/current";
		open PLAYLIST, ">$uthome/playlist";
	}
	if (@ARGV) {
		print PLAYLIST fullpath($_) . "\n" foreach @ARGV;
	} else {
		print PLAYLIST while <STDIN>;
	}
	updatelist();
} elsif ($cmd eq 'rm' or $cmd eq 'del') {
	open PLAYLIST, "$uthome/playlist";
	open PLAYLISTOUT, ">$uthome/playlist.newtmp";
	my @deletes;
	if (!@ARGV) {
		while (<STDIN>) {
			chomp;
			push @deletes, $_;
		}
	} else {
		@deletes = @ARGV;
	}
	while (my $file = <PLAYLIST>) {
		chomp $file;
		print PLAYLISTOUT "$file\n" unless grep { $_ eq $file } @deletes;
	}
	close PLAYLISTOUT;
	close PLAYLIST;
	rename "$uthome/playlist.newtmp", "$uthome/playlist";
	updatelist();
} elsif ($cmd eq 'clear') {
	open PLAYLIST, ">$uthome/playlist";
	close PLAYLIST;
	unlink "$uthome/current";
	updatelist();
} elsif ($cmd eq 'list') {
	open PLAYLIST, "$uthome/playlist";
	print while <PLAYLIST>;
	close PLAYLIST;
} elsif ($cmd eq 'play') {
	if ($pid) {
		print "Already playing.\n";
	} else {
		daemon();
	}
} elsif ($cmd eq 'next') {
	$current++;
	open CURRENT, ">$uthome/current";
	print CURRENT "$current\n";
	close CURRENT;
	updatelist();
	hurk();
} elsif ($cmd eq 'prev') {
	$current--;
	open CURRENT, ">$uthome/current";
	print CURRENT "$current\n";
	close CURRENT;
	updatelist();
	hurk();
} elsif ($cmd eq 'stop') {
	kill SIGTERM, $pid if $pid;
} elsif ($cmd =~ /-?h(elp)?/) {
	print <<EOD
utunes - a music player for UNIX hackers

usage: utunes [command] [files]

Commands are one of the following:
	play	begin playing the current playlist
	stop	stop playing the current playlist
	next	skip to the next song
	prev	skip to the previous song
	add	append files from the command line or stdin to the playlist
	load	replace the current playlist with files from stdin or command
		line
	rm	delete files from the command line or stdin from the
		playlist
	del	alias for rm
	clear	clear the current playlist
	list	dump the current playlist to stdout

If utunes is run without a command, the currently playing song is
printed to stdout.
EOD
}