#!/bin/sh

#######################
#
# Simple MP3 Jukebox
# KAWAMATA, Yoshihiro
# kaw@on.rim.or.jp
#
# $Id: player.sh,v 1.9 2005/08/08 13:24:29 kaw Exp $
#
#######################

#=====
# utility functions / routines
#=====

# trap for ^C
#
trap 'terminate "Aborted by user" ; exit' 2

# check flags,
# fetch a file,
# play it
#
# Usage: play_one_action { local_file | URL }
#
play_one_action () {
    [ $# -eq 0 ] && return

    # Controls
    #
    if [ -r ctrl/halt ]; then
	rm -f ctrl/halt
	terminate "Halting..."
	/sbin/shutdown -hp now
        sleep 86400

    elif [ -r ctrl/reboot ]; then
	rm -f ctrl/reboot
	terminate "Rebooting..."
	/sbin/shutdown -r now
        sleep 86400

    elif [ -r ctrl/restart ]; then
	rm -f ctrl/restart
	terminate "Restarting script..."
	exec $progfile

    elif [ -r ctrl/exit ]; then
	rm -f ctrl/exit
	terminate "Exiting..."
	exit
    fi

    # clean non-MP3 file and empty directories under spool
    #
    if [ -r ctrl/clean ]; then
	rm -f ctrl/clean
	echo "Clearning dirs..."
	find $src $dest oneshot oneshot_finished \( -type f -o -type l \) \! \( -iname '*.mp3' -o -iname '*.url' \) -print0 | xargs -0 rm -f
	for d in $src $dest oneshot oneshot_finished; do
	    (cd $d  && find . -type d -print | sort -r | xargs rmdir >/dev/null 2>&1)
	done
    fi

    # check if cache exceeds limit
    #
    if [ -r ctrl/checkcache ]; then
	rm -f ctrl/checkcache
	echo "Clearning cache dirs..."
	thresh=85
	durate=`df cache | tail -1 | awk '{ print 0+$5 }'`
	if [ $thresh -lt $durate ]; then
	    nfiles=`find cache -type f -print | wc -l`
	    if [ $nfiles -eq 1 ]; then
		rm -f `find cache -type f -print`
	    elif [ 1 -lt $nfiles ]; then
		rmfiles=`expr $nfiles / 2`
		ls -1t `find cache -type f -print` | tail -n $rmfiles | xargs rm -f
	    fi
	fi
    fi

    # pause playing
    #
    if [ -r ctrl/pause ]; then
	echo "Pausing..."
	while [ -r ctrl/pause ]; do
	  sleep 1
	done
    fi

    # play MP3 file
    #
    # ... invoke MP3 player
    #
    case "$1" in
	https://*.mp3|http://*.mp3|ftp://*.mp3)
	    echo "$1" > ctrl/playing/contents
            if [ -r ctrl/usecache ]; then
		md5=`echo -n "$1" | md5`
		if [ -r cache/$md5.mp3 ]; then
                    # play cache file rather than network
                    #
		    mpg123 -v $opts cache/$md5.mp3 2>&1 | tr \\015 \\012 | tail -n 2 > ctrl/playing/endstatus
                else
	            # tee is for both to save MP3 to cache
	            # and to buffer data in the pipeline
	            #
		    ftp -o - "$1" | tee cache/$md5.mp3 | mpg123 -v $opts - 2>&1 | tr \\015 \\012 | tail -n 5 > ctrl/playing/endstatus
	            # abnormal end status - remove cache
	            #
		    [ -z cache/$md5.mp3 ] && rm -f cache/$md5.mp3
                fi
	    else
	        # tee is simply for buffer
	        #
		ftp -o - "$1" | tee | mpg123 -v $opts - 2>&1 | tr \\015 \\012 | tail -n 5 > ctrl/playing/endstatus
	    fi
	    ;;
	*.[Mm][Pp]3)
	    if [ -r "$1" ]; then
		echo "$1" > ctrl/playing/contents
		mpg123 -v $opts "$1" 2>&1 | tr \\015 \\012 | tail -n 2 > ctrl/playing/endstatus
	    else
		echo "Cannot find $1"
	    fi
	    ;;
    esac
    echo "======END STATUS======"
    cat ctrl/playing/endstatus
    echo "\n======================"
}


# move played file to another directory.
# directory hierarchy preserved.
#
# Usage: movefile srcpath desttop
#
movefile () {
    [ $# -lt 2 ] && return
    [ -r $1 ] || return
    destdir=$2/${1#*/}
    destdir=${destdir%[^/]*}
    if [ -d $destdir ]; then
	mv $1 $destdir
    else
	mkdir -p $destdir && mv $1 $destdir
    fi
}

# terminate process
#
terminate () {
    [ $# -ne 0 ] &&  echo "$@"
    rm -rf ctrl/playing
    [ -r ctrl/finish ] && . ctrl/finish
    kill -TERM $fwatchpid
}


#=====
# Running code from here
#=====

# Base and current directory is where script placed.
#
progfile=$0
cd `dirname $progfile` || exit 1

# check if another player running
#
if mkdir ctrl/playing; then
    date > ctrl/playing/started
    chmod 0755 ctrl/playing
else
    echo "Another player running"
    exit 1
fi

# remove stale files
#
rm -f ctrl/exit ctrl/reboot ctrl/halt ctrl/mute ctrl/loud

# user initialization
#
[ -r ctrl/init ] && . ctrl/init

# invoke event handler
#
fwatch ctrl ./asyncevent.sh &
fwatchpid=$!

#=====
# main loop ... play MP3 and its control
#=====
while :; do
    # parameters to MP3 player
    #
    unset opts
    [ -r ctrl/mpg123opts ] && opts=`cat ctrl/mpg123opts`

    # We have two spool directories for infinite loop
    # ... spool0 and spool1.
    #
    # set direction of file movement
    # between two spools
    # when every MP3 file played
    #
    if [ -r ctrl/reverse ]; then
	 src=spool1
	dest=spool0
    else
	 src=spool0
	dest=spool1
    fi

    # playlist is symlink to spool for playing.
    #
    rm -f playlist
    ln -s $src playlist

    # for every MP3 files
    #	control and/or playing
    #
    # xNOPLAY is a sentinel to run inner loop
    #
    for playfile in xNOPLAY `find $src \( -type f -o -type l \) \( -iname '*.mp3' -o -iname '*.url' \) | sort`; do

	# retrieve resource location
	#
	case "$playfile" in
	    *.[Uu][Rr][Ll])
		mp3reslist=`cat $playfile`
		;;
	    *.[Mm][Pp]3|xNOPLAY)
		mp3reslist=$playfile
		;;
	esac

	# for every MP3 resource ...
	#
	for mp3res in $mp3reslist; do

	    # perform one shot playlist
	    #
	    while :; do
		for oneshot in xNOPLAY `find oneshot \( -type f -o -type l \) \( -iname '*.mp3' -o -iname '*.url' \) | sort`; do
		    # retrieve resource location
		    #
		    case "$oneshot" in
			*.[Uu][Rr][Ll])
			    oneshot_mp3res=`cat $oneshot`
			    ;;
			*.[Mm][Pp]3|xNOPLAY)
			    oneshot_mp3res=$oneshot
			    ;;
		    esac

		    for os_mp3 in $oneshot_mp3res; do

		        # check if resource file removed
		        #
			if [ -r $oneshot ]; then
			    play_one_action $os_mp3
			fi

		        # rescan all contents
		        #
			if [ -r ctrl/rescan ]; then
			    echo "Rescanning playlist..."
			    break 5
			fi
		    done

		    movefile $oneshot oneshot_finished
		done
		[ X"$oneshot" = XxNOPLAY ] && break
	    done

	    play_one_action $mp3res

	    # restart at current spool
	    #
	    if [ -r ctrl/rescan ]; then
		echo "Rescanning playlist..."
		break 2
	    fi
	done

	# move played file to the other spool
	#
	movefile $playfile $dest
    done

    # pause when no MP3 files in spools
    #
    if [ X"$playfile$oneshot" = XxNOPLAYxNOPLAY ]; then
	noplay=Z$noplay
	echo "No file to play : $noplay"
	if [ $noplay = ZZ ]; then
	    echo "Now sleeping for 60 secs..."
	    sleep 60
	    noplay=""
	fi
    else
	noplay=""
    fi

    # role change of spools
    # (not changing when rescan)
    #
    if [ -r ctrl/rescan ]; then
	rm -f ctrl/rescan
    else
	if [ -r ctrl/reverse ]; then
	    rm -f ctrl/reverse
	else
	    touch ctrl/reverse
	fi
    fi
done
