#!/bin/bash
# bash-music v1.0
#
# Bash script to generate a playlist and play music!
# It's not great, but it's a great basic player,
# and can take advantage of Pulseaudio's networked playing.
# Fairly small memory usage too.

# Requires:
# bash v3+
# gzip, zcat (provided by gzip)
# play (provided by sox)
# tree, cut, sed, tail, head, sort, kill, that sort of stuff...

# Shortcomings:
# Playlist will contain non-audio items
# Max 32767 songs due to bash's $RANDOM limit
# Does not daemonize and does not catch any input, only signals,
#+i.e. it's fairly simple

# Syntax:
# ./bash-music
# ./bash-music skip

# Config -- make sure you edit this
PIDFILE="/tmp/bash-music.pid"
PLFILE="/tmp/bash-music-pl.gz"
MUSDIR="/path/to/your/Music"
TREECMD="tree -qQinflL 10 --noreport"
# Bash's maximum random number through $RANDOM
MAXRND=32767
MAXERR=50

# Begin

# Did we receive a command?
if [ -e "$PIDFILE" ] ; then
	if [ -r "$PIDFILE" ] ; then
		if [ "$1" == "" ] ; then
			echo "$0 is already running."
			exit 0
		elif [ "$1" == "skip" ] ; then
			kill -USR1 `cat "$PIDFILE"`
			exit 0
		fi
	fi
fi

# Init
echo $$ > "$PIDFILE"
ERRS=0
umask 0077
# X is not necessary here and can cause spurious output, esp. with X-forwarding
unset DISPLAY

# Set traps
trap 'rm -f "$PLFILE" "$PIDFILE"; exit 0' INT TERM EXIT
trap 'kill $PLAYPID' USR1

# Generate playlist,
#	prepend a random number to each entry
#	sort it by the random numbers
#	trim out the random numbers
#	compress and save list

echo "Generating playlist, please wait..."
$TREECMD "$MUSDIR" | \
	sort -R | \
	head -n $MAXRND | \
	cut -f2- | \
	gzip -9 > "$PLFILE"

# How long is our playlist?
CEIL=`zcat "$PLFILE" | wc -l`

# Now play!
# .. lack of a better syntax makes me sad :<
# (syntaxes such as `for i in {1..5}` do not allow for variables)
i=0
while [ $i -lt $CEIL ]
do
	SONG=.
	while [ ! -f "$SONG" -o ! -r "$SONG" ] ; do
		RND=$RANDOM
		if [ "$RND" -gt "$CEIL" ] ; then let "RND %= $CEIL" ; fi
		SONG=`zcat "$PLFILE" | sed -n "$RND"p | head -c -2 | tail -c +2`
	done
	play -q "$SONG" -t pulseaudio &
	let PLAYPID=$!
	echo "#$i: $PLAYPID: Now playing $SONG"
	wait $PLAYPID
	if [ $? -ne 0 ] ; then let "ERRS=$ERRS+1" ; fi
	if [ $ERRS -gt $MAXERR ] ; then
		echo "Too many errors; aborting!"
		exit 1
	fi
	let i=i+1;
done
