#!/bin/bash

# This script uses GJay <http://gjay.sourceforge.net/>
# and mpc <http://www.MusicPD.org/?page=mpc> to automatically
# and intelligently add songs to the MPD playlist.
#
# The idea here is to basically have an MPD-based player
# default to the ease of being `like your favourite radio station'
# but then provide the user with the ability to make manual selections
# and have those feed into the DJ's decision-making process.


# Default parameters (overridable; cf. the `getopt' call, further down):

musicdir=/var # Where GJay thinks the audio-files are rooted;
              # this prefix will be stripped from file-paths
              # before they're passed to MPD, so that, e.g.:
              # music in /var/music/* will be passed as
              # `music/*'.

gjay_qdelay=60 # How many seconds it takes for GJay to finish
               # (this will vary, depending on the size of
               #  your music-library--larger libraries take longer;
               #  for a 3000-song library on the Ben NanoNote,
               #  it takes about 60 seconds to generate a playlist).
               # It's fine to artificially inflate/deflate this,
               # depending on how much you care about `skip track'
               # buffering; different MPD UIs lead to different
               # user-behaviour, which leads to different
               # decisions here....

decision_time=10 # How many seconds it takes for the user
                 # to decide to skip a track....

gjseed_file=/var/run/gjseed


# Parse overrides:
eval set -- \
    $(getopt --options="m:q:d:s:h" \
             --longoptions="music-root:" \
             --longoptions="queue-delay:" \
             --longoptions="decision-time:" \
             --longoptions="seed-state:" \
             --longoptions="help" \
             -- "$@")
while true
do
    OPT="$1"; shift

    case "$OPT" in
        --queue-delay)
            gjay_qdelay="$1"; shift;;
        --decision-time)
            decision_time="$1"; shift;;
        --music-root)
            musicdir="$1"; shift;;
        --seed-state)
            gjseed_file="$1"; shift;;
        --)
            break;;
        --help)
            cat<<EOF
Usage: mpdjay [options ...]

Options:
    --queue-delay=SECONDS   (default: $gjay_qdelay)
        The time GJay can be expected to take
        before it outputs a new playlist.

    --decision-time=SECONDS (default: $decision_time)
        The time that users are assumed to take
        deciding to skip a given song.

    --music-root=PATH       (default: $musicdir)
        This path prefix is stripped from song-file paths
        before they're passed to "gjay --file=...".

    --seed-state=PATH       (default: $gjseed_file)
        The path of a state-file used to track GJay's
        current seed-song.
EOF
            exit 0
    esac
done

# Derived parameters:

songcount_prebuffer=$((gjay_qdelay/decision_time + 1))

(echo qman-startup; mpc idleloop) \
| while read event
do
    if [ "$event" = "mixer" ]
    then
        continue
    fi

    echo event=$event
    mpc status

# We want to be able to support rapid forward-skip without any apparent end,
# so we should really queue some new songs before we hit the end;
# HOW MANY depends on how long it takes to queue them and how quickly
# the user can decide to skip forward (presumably without looking
# at the playlist; if they're looking, they won't go past the end, right?).
# If it takes 20 seconds to generate a playlist, and 2 seconds
# to decide to listen to enough of a song to decide to skip it,
# then we should `top up' the queue at least 10 songs in advance.
    (mpc status \
     | egrep '\brepeat: off\b' \
     | egrep '\brandom: off\b' \
     | egrep '\bsingle: off\b' \
     > /dev/null \
     && \
     mpc status) \
    | egrep '^\[playing\] #([[:digit:]]+)/([[:digit:]]+)' \
    | sed -re 's|^\[playing\] #([[:digit:]]+)/([[:digit:]]+) .*|\1 \2|' \
    | if read curr total && [ $curr -gt $((total-songcount_prebuffer)) ]
    then
        gjseed=$(cat $gjseed_file)
        songfile=$(mpc playlist --format=%file% | tail -n1)
	if [ "$songfile" != "$gjseed" ] || ! killall -q -0 gjay
	then
	    killall -q gjay
	    echo "$songfile" > $gjseed_file

            echo "Calling GJay for a playlist starting with $songfile..."

            time \
            gjay --playlist --file="$musicdir/$songfile" \
            | sed -e 1d \
            | sed -e "s|$musicdir/||" \
            | mpc --verbose add &

	    gjpid=$!
	fi
    else
        echo "Queue is sufficiently full."
	killall -q gjay
    fi

    echo
done

# Since GJay may still be running in the background,
# loading a huge DB, keeping a removable disk from
# being un-mounted..., kill it:
killall gjay

# We'll exit if MPD is shut-down (and closes our socket);
# in case we're being run in a loop, don't beat too hard:

sleep 2
