#! /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