#! /bin/zsh
# This file is maintained at http://git.mdcc.cx/draai
# draai - Joost van Baal-Ilić's music playing stuff
# see also
# joostvb@kovalevskaya:~% grep mpc .zshrc_local
# copyright:
COPYRIGHT='Copyright (C) 2006 - 2011 Joost van Baal, 2011 - 2015 Joost van Baal-Ilić'
# This program is freely distributable per the following license:
LICENSE="
Draai is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License, as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version. This program is distributed WITHOUT ANY WARRANTY. You should have
received a copy of the GNU General Public License along with draai. If not,
see ."
VERSION='@VERSION@'
prog=`basename $0`
setopt extendedglob
# FIXME: default, rc, env, commandline
test -r $HOME/.draai/rc && . $HOME/.draai/rc
debug=${DR_DEBUG:-false}
shuffle=${DR_SHUFFLE:-true}
timestamp=${DR_TIMESTAMP:-true}
raw=${DR_RAW:-false}
sloppy=${DR_SLOPPY:-false}
shuffle_native=${DR_SHUFFLE_NATIVE:-false}
# DR_SHUFFLE_NATIVE: we want mpc/mpd's shuffle, not dr_unsort's unsort
# FIXME used in dr_watch(1) only
# sleep=${DR_SLEEP:-2}
sleepcmd=${DR_SLEEP_CMD:-"mpc idle"}
# mpd.conf:
# metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc,comment"
rawformat="%position%) %file%: [[%artist% - ]%album% - %title% ][(%comment%)]([%name% - ][%genre% - ][%date% - ][%composer% - ][%performer% - ][%disc% - ][%track% ]%time%)"
format="[%position%) ][[[%artist% - ]%album% - %title%]|[%file%[ - %name%][ - %title%]]] (%time%)"
# format="[%position%) ][[[%artist% - ]%album% - %title%]|[%file%]] ([%name% - ]%time%)"
# draai list | grep '()'
crossfade=${DR_CROSSFADE:-4}
peek=${DR_PEEK:-30}
seek=${DR_SEEK:-20}
volume_step=${DR_VOLUME_STEP:-10}
fadeout_step=${DR_FADEOUT_STEP:-10}
fadeout_repeat=${DR_FADEOUT_REPEAT:-10}
fadeout_sleep=${DR_FADEOUT_SLEEP:-0.2}
syslog_file=${DR_SYSLOG_FILE:-/var/log/syslog}
syslog_facility=${DR_SYSLOG_FACILITY:-user}
syslog_level=${DR_SYSLOG_LEVEL:-info}
ncmpcpp=${DR_INIT_NCMPCPP:-false}
initwatch=${DR_INIT_WATCH:-false}
usage()
{
cat < /dev/null
# GNU coreutils
sleep $fadeout_sleep
done
## exponential fadeout:
## don't do that, it's way too fast
#
## # 1024 = 2^10
## step=$(( $vol / 1024 ))
## mpc volume -$step > /dev/null
## sleep 0.2
## repeat 8
## do
## mpc volume -$step > /dev/null
## step=$(( step * 2 ))
## sleep 0.2
## done
}
# calculate position of current track
position()
{
for l in $(mpc | grep playing)
do
# zshexpn(1) "FILENAME GENERATION"
if [[ $l == (#b)\#(*)/* ]]
then
position=$match
fi
done
$debug && echo >&2 $prog: set position to \'$position\'
}
# see zsh-betabuiltins(1) for getopts help
# see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=384014 for manpage
# http://zsh.dotsrc.org/Doc/Release/zsh_16.html#SEC89
# getopt(1) understands long options. use that one?
# dpigs(1) has nice getopt(1) example
OPTS=$(getopt -o VhdSp:n:t:Trsl --long version,help,debug,noshuffle,playlist:,number:,time:,notime,raw,sloppy,license -- "$@")
eval set -- "$OPTS"
# define playlists to be an (ordinary) array
typeset -a playlists
playlists=()
while true; do
case "$1" in
-V|--version)
echo $prog $VERSION
exit 0
;;
-h|--help)
usage
exit 0
;;
-q|--quiet)
quiet=true
shift
;;
-d|--debug)
debug=true
shift
;;
-S|--noshuffle)
shuffle=false
shift
;;
-p|--playlist)
playlists=($playlists "$2")
shift 2
;;
-n|--number)
n="$2"
shift 2
;;
-t|--time)
time="$2"
shift 2
;;
-T|--notime)
timestamp=false
shift
;;
-r|--raw)
raw=true
shift
;;
-s|--sloppy)
sloppy=true
shift
;;
-l|--license)
echo $COPYRIGHT
echo $LICENSE
exit 0
;;
--)
shift
break
;;
*)
usage
exit 1
;;
esac
done
shift $(($OPTIND - 1))
# define args to be an associative array
typeset -A args
command=$1
shift
$debug && echo >&2 $prog: peek is \'$peek\'
case $command in
draai)
# no other args:
# drDefault(plfiles=options.playlists, n=options.n, shuffle=options.shuffle)
# some files present:
# drDraai(args, n=options.n, shuffle=options.shuffle)
set -A files "$@"
if test -n "$time"
then
opts=
$debug && opts="--debug"
$shuffle || opts="$opts --noshuffle"
test -n "$n" && opts="$opts --number $n"
if test -n "$playlists"
then
for playlist in $playlists
do
opts="$opts --playlist $playlist"
done
fi
if [[ $time = (#b)+(<->) ]]
then
# fixme: more strict match
echo draai $opts draai $files | at now + $match minutes
else
echo draai $opts draai $files | at $time
fi
else
# mpc clear > /dev/null
mpc crop > /dev/null
$debug && echo >&2 "$prog: gonna add $files"
for f in $files
do
mpc add "$f"
$debug && echo >&2 $prog: adding \'$f\'
done
# for some reason, piping to mpc with ... echo $f; done | mpc add ... fails
for playlist in $playlists
do
mpc load "$playlist" | grep -v volume
done
$shuffle && mpc shuffle > /dev/null && mpc random off > /dev/null
mpc crossfade $crossfade > /dev/null
mpc play
fi
;;
watch)
DR_SLEEP_CMD="$sleepcmd" dr_watch draai peek
;;
init|genesis)
# user should be permitted to write on tty12. on Debian systems, this
# means being a member of group tty. on systems running systemd, this
# will probably fail
ttymember=false
# will fail if current user is member of a group called 'foo tty'
# id as shipped with GNU coreutils
for g in $(id --name --groups)
do
test tty = "$g" && ttymember=true
done
$ttymember && draai tail >/dev/tty12 &
draai syslog &
if test -n "$DISPLAY"
then
x-terminal-emulator -e draai logtail &
x-terminal-emulator -e draai watch &
$ncmpcpp && x-terminal-emulator -e ncmpcpp &
$initwatch && DR_SLEEP_CMD="$sleepcmd" x-terminal-emulator -e dr_watch mpc &
else
# use "screen improved" by some debian guy
# screen draai tail
# FIXME dr_watch()
screen -S draai -U dr_watch draai peek
# "create"
# exec
# DR_PEEK=10 screen -S draai -U dr_watch draai peek
# screen -p draai -X c
# screen -R (debian-specific)
# -d -m
fi
;;
next|volgende)
# drNext(plfile=drDefaultNextPLFile, n=options.n)
# next($args)
echo not yet implemented
;;
guestlist)
# FIXME we assume all args are >> current position
if test $# -gt 0
then
position=0
position
shift=0
for p in "$@"
do
echo $p
done | sort --numeric-sort --reverse | while read p
do
position=$(($position + 1 ))
p=$(( $p + $shift ))
$debug && echo >&2 "$prog: gonna move $p to $position"
mpc move $p $position
shift=$(( $shift + 1 ))
done
else
echo "$prog: command guestlist needs tracknumber(s)"
exit 1
fi
;;
unguestlist|unfriend|ontvriend)
if test $# -gt 0
then
position=$( mpc playlist | tail -1 | cut -d\) -f1 )
for p in "$@"
do
echo $p
done | sort --numeric-sort --reverse | while read p
do
$debug && echo >&2 "$prog: gonna move $p to $position"
mpc move $p $position
done
else
echo "$prog: command unguestlist needs tracknumber(s)"
exit 1
fi
;;
insert|voegin)
if test $# -gt 0
then
set -A files "$@"
$debug && echo >&2 $prog: gonna add \'$files\'
for f in $files
do
$debug && echo >&2 $prog: gonna add \'$f\'
mpc add "$f"
position=$( mpc playlist | tail -1 | cut -d\) -f1 )
draai guestlist $position
done
else
# reads from stdin
mpc insert
fi
;;
move)
if test $# -eq 2
then
mpc move "$@"
else
echo "$prog: command move needs 2 tracknumbers"
exit 1
fi
;;
search|zoek)
mpc search any "$@"
;;
nice)
# drNice(args[1:], plfiles=options.playlists, n=options.n, shuffle=options.shuffle)
echo not yet implemented
;;
skip|slaover)
# skip current song
$sloppy || fadeout
mpc next
# FIXME: is NOT sleeping really "sloppy"!?
$sloppy || sleep $fadeout_sleep
# restore original volume
$sloppy || mpc volume $volume >/dev/null
;;
zap)
# zap to track (or, more typically, radiostation) which is number
# in current playlist
if test $# -eq 1
then
mpc move $1 1
mpc play 1
else
echo "$prog: command zap needs one tracknumber"
exit 1
fi
;;
osjittisstil)
draai --sloppy skip
;;
delete)
if test $# -gt 0
then
for p in "$@"
do
echo $p
done | sort --numeric-sort --reverse | while read p
do
mpc del $p
done
else
position=0
position
mpc del $(( $position + 1 ))
fi
;;
fastforward|voorwaarts)
mpc seek +$seek%
;;
crescendo|harder|louder)
mpc volume +$volume_step
;;
diminuendo|zachter|softer)
mpc volume -$volume_step
;;
shuffle|schud)
$shuffle || echo 'warning: overruling DR_SHUFFLE'
if $sloppy or $shuffle_native
then
mpc shuffle
else
# dr_unsort needs filesystem level write access to mpd's playlist directory
dr_unsort || echo 'error: calling dr_unsort failed. no write access?'
fi
;;
listall|toonalles|toonalleliedjes)
# merely empty wrapper
mpc listall
;;
add|voegtoe)
if test $# -gt 0
then
set -A files "$@"
$debug && echo >&2 $prog: gonna add \'$files\'
for f in $files
do
$debug && echo >&2 $prog: gonna add \'$f\'
mpc add "$f"
done
else
# reads from stdin
mpc add
fi
;;
tail|staart)
# tail: like tail -f
# FIXME use variables for 2 and 74 e.a.
old=""
# FIXME do not sleep, but 'mpc idle' (mpc idle playlist , mpc idle player )
# while sleep 2; do
while mpc idle >/dev/null; do
current=$(mpc --format $format | head -1)
$raw && string=$(mpc --format $rawformat | head -1)
if test "$old" != "$current"
then
if $raw
then
echo $string
else
echo -n $(date +%H:%M)
# 80 - 5 = 75 columns left: 1 for space, 3 for dots, 41 + 30 for rest
# see zshmisc PROMPT EXPANSION / EXPANSION OF PROMPT SEQUENCES
# %G Within a %{...%} sequence, include a `glitch': that
# is, assume that a single character width will be
# output.
# c With ${#name}, count the total number of characters in an
# array, as if the elements were concatenated with spaces
# between them.
# %>string> Specifies truncation behaviour for the remainder
# of the prompt string.
# Sun 18 17:09 < Fruit> $#foo
# Sun 18 17:10 < Fruit> maar dat is stiekem stuk
# Sun 18 17:10 < Fruit> want dat geeft je het aantal bytes
# Sun 18 17:10 < Fruit> ipv het aantal karakters
# only truncate if $current > 74 chars.
# if <= 74 chars: line out to left
if [[ $#current -gt 74 ]]
then
# FIXME fails when playing from radio station
print -P " %41>>$current%>>...%30<<$current"
else
# parse current: Guitar - Mind The Gap Volume 61 - Tokyo Memory (5:19)
# split off timespec: rightadjust
# time is "12:16)"
time=$(echo $current | sed 's/.*(//')
track=$(echo $current | sed 's/^\(.*\)(.*$/\1/')
# we assume time is not wider than (234:67)
# AFX - Analord 08 - Backdoor.Spyboter.A (125:06)
# -66- ( -7-
printf " %-66.66s%8.8s\n" "$track" "($time"
fi
fi
old=$current
fi
done
# alternatively, one might like to run
# watch --no-title 'mpc --format "[[[%artist% - ]%album% - %title%]|[%file%]] ([%name% - ][%track% ]%time%)"'
;;
logtail)
tail -F $syslog_file | grep ' draai\['
;;
syslog)
draai --raw tail | logger -i -p $syslog_facility.$syslog_level -t draai
;;
list)
# list current playlist
$raw && format=$rawformat
mpc --format $format playlist
;;
peek|spiek)
# show status of current song
mpc | grep playing
# show upcoming $peek tracks
position=0
position
total=$(mpc playlist | wc -l)
$raw && format=$rawformat
$debug && echo >&2 $prog: peek is \'$peek\'
mpc --format $format playlist | tail -$(( $total - $position + 1 )) | head -$peek | \
while read current
do
time=$(echo $current | sed 's/.*(//')
track=$(echo $current | sed 's/^\(.*\)(.*$/\1/')
if [[ $#track -gt 74 ]]
then
if [[ $track == \>* ]]
then
print -nP "%47>>$track%>>...%22<<$track"
else
print -nP " %46>>$track%>>...%22<<$track"
fi
else
if [[ $track == \>* ]]
then
printf "%-72.72s" "$track"
else
printf " %-71.71s" "$track"
fi
fi
# we assume time is not wider than (234:67)
printf "%8s\n" "($time"
done
;;
play|unpause|resume|ga)
if test -n "$time"
then
if [[ $time = (#b)+(<->) ]]
then
# fixme: more strict match
echo draai play | at now + $match minutes
else
echo draai play | at $time
fi
else
mpc play
fi
;;
quit|exit|stop|pause|ho)
if test -n "$time"
then
if [[ $time = (#b)+(<->) ]]
then
# fixme: more strict match
echo draai quit | at now + $match minutes
else
echo draai quit | at $time
fi
else
fadeout
if [[ "$command" == pause ]]
then
mpc pause
else
mpc stop > /dev/null
fi
# restore original volume
mpc volume $volume > /dev/null
fi
;;
*)
usage
exit 1
;;
esac