From db947a1f555eab19b87cd540c831d1fc30310025 Mon Sep 17 00:00:00 2001 From: katherine Date: Wed, 19 Dec 2018 17:35:39 -0700 Subject: add fancy new functions, with shared helpers --- .config/init/funcs/audio-concat | 127 +++++++++++++++++++ .config/init/funcs/audio-convert | 233 +++++++++++++++++++++++++++++++++++ .config/init/funcs/mpd-cover-convert | 8 +- .config/init/funcs/scap | 4 +- 4 files changed, 368 insertions(+), 4 deletions(-) create mode 100755 .config/init/funcs/audio-concat create mode 100755 .config/init/funcs/audio-convert (limited to '.config/init/funcs') diff --git a/.config/init/funcs/audio-concat b/.config/init/funcs/audio-concat new file mode 100755 index 0000000..73b3ae7 --- /dev/null +++ b/.config/init/funcs/audio-concat @@ -0,0 +1,127 @@ +#!/usr/bin/env zsh +# concatenate multiple audio files into one + +source "$HOME/.config/init/helpers" || exit 1 + +local callstr=$0 + +usage() { + echo "Usage: $callstr [OPTIONS...] ..." + echo "Concatenate multiple audio files into one" + echo "" + echo " \e[1mdescription opt longform arg default\e[0m" + echo " specify destination file -o --outfile out.ogg" + echo " specify output bitrate -b --bitrate k (opus:35k, vorbis:50k, mp3:65k, flac:N/A)" + echo " workaround for many inputs -m --many" + echo " print this help -h --help" + exit 0 +} + +trap_abort() { + echo "" + abort "process interrupted" +} + +trap 'trap_abort' SIGABRT SIGHUP SIGINT SIGQUIT SIGTERM + +local dest='out.opus' + +local destpat='^.*\.(opus|mp3|ogg|flac)$' +local bitratepat='^[1-9][0-9]*k$' + +local filterstr +local codecstr +local bitratestr +local many + +local parseerr=$(2>&1 zparseopts -D -E -M -A args \ + o: -outfile:=o \ + b: -bitrate:=b \ + m -many=m \ + h -help=h | cut -d ' ' -f 2-) + +[[ -z $parseerr ]] || abort $parseerr + +zparseopts -D -E -M -A args \ + o: -outfile:=o \ + b: -bitrate:=b \ + m -many=m \ + h -help=h + +local optarg +for opt in ${(@k)args}; do + unset optarg + [[ -z $args[$opt] ]] || optarg=$args[$opt] + case $opt in + -o) [[ $optarg =~ $destpat ]] || \ + abort "destination file must be of type opus, mp3, or ogg" + dest=$optarg + [[ ${optarg:e} == 'opus' ]] && codecstr='libopus' \ + && [[ -z $bitratestr ]] && bitratestr='35k' + [[ ${optarg:e} == 'mp3' ]] && codecstr='libmp3lame' \ + && [[ -z $bitratestr ]] && bitratestr='65k' + [[ ${optarg:e} == 'ogg' ]] && codecstr='libvorbis' \ + && [[ -z $bitratestr ]] && bitratestr='50k' + [[ ${optarg:e} == 'flac' ]] && codecstr='flac' \ + && [[ -z $bitratestr ]] && bitratestr='' + ;; + -b) [[ $optarg =~ $bitratepat ]] || \ + abort "bitrate must positive integer + k, for kbps" + bitratestr=$optarg + ;; + -m) many=true ;; + -h) usage ;; + esac +done + +[[ -z $codecstr ]] && codecstr='libopus' +[[ -z $bitratestr ]] && bitratestr='35k' + +[[ $codecstr == "flac" ]] && bitratestr='' + +[[ ${#@} -eq 0 ]] && abort "no input files specified" + +if [[ -e $dest ]]; then + local delprompt + printf "destination file \`$dest\` exists\ndelete it? [y/N]: " + read -q delprompt + echo "" + [[ $delprompt = "y" ]] || return 0 + 2>/dev/null rm $dest || abort "could not delete file \`$dest\`" +fi + +local pid + +if [[ -z $many ]]; then + local inputlist=() + local i=0 + for f in $@; do + inputlist+=(-i $f) + filterstr="${filterstr}[${i}:a:0] " + i=$((i+1)) + done + + filterstr="${filterstr}concat=n=${i}:v=0:a=1 [outa]" + + ( + ffmpeg -loglevel -8 $inputlist \ + -filter_complex $filterstr -map '[outa]' \ + -c:a $codecstr ${bitratestr/*k/-b:a} $bitratestr \ + -map_metadata -1 $dest + ) & + pid=$! +else + ( + for f in $@; do + ffmpeg -loglevel -8 -i $f -f s16le -ar 44.1k -ac 2 pipe: + done | ffmpeg -loglevel -8 -f s16le -ar 44.1k -ac 2 -i pipe: \ + -c:a $codecstr ${bitratestr/*k/-b:a} $bitratestr \ + -map_metadata -1 $dest + ) & + pid=$! +fi + +local plural +[[ $#@ -ne 1 ]] && plural='s' + +wait-anim $pid "concatenating $#@ file$plural to \`$dest\`" diff --git a/.config/init/funcs/audio-convert b/.config/init/funcs/audio-convert new file mode 100755 index 0000000..4923baa --- /dev/null +++ b/.config/init/funcs/audio-convert @@ -0,0 +1,233 @@ +#!/usr/bin/env zsh +# concatenate multiple audio files into one + +source "$HOME/.config/init/helpers" || exit 1 + +local callstr=$0 + +usage() { + echo "Usage: $callstr [OPTIONS...] ..." + echo "Convert multiple audio files to a new format" + echo "" + echo " \e[1mdescription opt longform arg default\e[0m" + echo " output codec -c --codec opus" + echo " output bitrate -b --bitrate k (opus:35k, vorbis:50k, mp3:65k, flac:N/A)" + echo " output quality -q --quality N/A" + echo " output directory -d --dest ." + echo " copy metadata -m --metadata false" + echo " background job count -j --jobs 4" + echo " print this help -h --help" + exit 0 +} + +local fifoname +local fifomade=0 +local pid + +trap_abort() { + echo "" + print-error "process interrupted" + # race condition-y, but eh + repeat 5; do + [[ -z $pid ]] && sleep 0.1 || break + done + [[ -z $pid ]] || kill -s SIGKILL $pid + [[ -p $fifoname ]] && [[ $fifomade -eq 1 ]] && 2>/dev/null rm $fifoname + exit 1 +} + +trap 'trap_abort' SIGABRT SIGHUP SIGINT SIGQUIT SIGTERM + +local codecpat='^(opus|mp3|vorbis|flac)$' +local bitratepat='^[1-9][0-9]*k$' +local metadatapat='^(true|false)$' +local jobcountpat='^[1-9][0-9]*$' + +local qpatvorbis='^([0-9]|10)$' +local qpatmp3='^[0-9]$' + +local codecstr +local codecprintstr +local extstr +local bitratestr +local bitrateset +local metastr +local dirstr +local qstr + +local jobcount + +local parseerr=$(2>&1 zparseopts -D -E -M -A args \ + c: -codec:=c \ + b: -bitrate:=b \ + q: -quality:=q \ + d: -dest:=d -destination:=d \ + j: -jobs:=j \ + m:: -metadata::=m \ + h -help=h | cut -d ' ' -f 2-) + +[[ -z $parseerr ]] || abort $parseerr + +zparseopts -D -E -M -A args \ + c: -codec:=c \ + b: -bitrate:=b \ + q: -quality:=q \ + d: -dest:=d -destination:=d \ + j: -jobs:=j \ + m:: -metadata::=m \ + h -help=h + +local optarg +local opt +for opt in ${(@k)args}; do + unset optarg + [[ -z $args[$opt] ]] || optarg=$args[$opt] + case $opt in + -c) [[ $optarg =~ $codecpat ]] || \ + abort "codec must be one of \`opus\`, \`mp3\`, \`flac\`, or \`vorbis\`" + [[ ${optarg} == 'opus' ]] \ + && codecprintstr=${optarg} \ + && codecstr='libopus' && extstr='opus' \ + && [[ -z $bitratestr ]] && bitratestr='35k' + [[ ${optarg} == 'mp3' ]] \ + && codecprintstr=${optarg} \ + && codecstr='libmp3lame' && extstr='mp3' \ + && [[ -z $bitratestr ]] && bitratestr='65k' + [[ ${optarg} == 'vorbis' ]] \ + && codecprintstr=${optarg} \ + && codecstr='libvorbis' && extstr='ogg' \ + && [[ -z $bitratestr ]] && bitratestr='50k' + [[ ${optarg} == 'flac' ]] \ + && codecprintstr=${optarg} \ + && codecstr='flac' && extstr='flac' \ + && [[ -z $bitratestr ]] && bitratestr='' + ;; + -b) [[ $optarg =~ $bitratepat ]] || \ + abort "bitrate must positive integer + k, for kbps" + bitratestr=$optarg + bitrateset=1 + ;; + -q) qstr=$optarg ;; + -d) dirstr=$optarg ;; + -j) [[ $optarg =~ $jobcountpat ]] || \ + abort "job count must be a non-negative integer" + jobcount=$optarg + ;; + -m) if [[ $optarg ]]; then + [[ $optarg =~ $metadatapat ]] || \ + abort "metadata must be either \`true\` or \`false\`" + + [[ $optarg == "false" ]] && metastr="" \ + || metastr=("-map_metadata" "-1") + else + metastr=("-map_metadata" "-1") + fi + ;; + -h) usage ;; + esac +done + +[[ -z $codecstr ]] && codecstr='libopus' +[[ -z $bitratestr ]] && bitratestr='35k' +[[ -z $codecprintstr ]] && codecprintstr='opus' +[[ -z $extstr ]] && extstr='opus' +[[ -z $jobcount ]] && jobcount=4 +[[ -z $dirstr ]] && dirstr='.' +[[ -z $metastr ]] && metastr=("-map_metadata" "-1") + +if [[ ! -z $qstr ]]; then + [[ -z $bitratset ]] || abort "-b and -q are not compatible" + [[ $codecstr == "libopus" ]] && abort "-q cannot be used with opus" + [[ $codecstr == "libflac" ]] && abort "-q cannot be used with flac" + [[ $codecstr == "libvorbis" ]] && [[ ! $qstr =~ $qpatvorbis ]] \ + && abort "for vorbis, -q must be in the range [0-10]" + [[ $codecstr == "libmp3lame" ]] && [[ ! $qstr =~ $qpatmp3 ]] \ + && abort "for mp3, -q must be in the range [0-9]" +else +fi + +[[ $codecstr == "flac" ]] && bitratestr='' + +[[ $#@ -eq 0 ]] && abort "no input files specified" + +local f +for f in $@; do + [[ -f $f ]] || abort "could not read file \`$f\`" +done + +2>/dev/null mkdir -p $dirstr || abort "could not write to directory \`$dirstr\`" + +repeat 5; do + fifoname="$dirstr/audio-convert-tmp-$RANDOM" + [[ -e $fifoname ]] && continue + 2>/dev/null mkfifo $fifoname || return 1 + fifomade=1 + break +done + +[[ $fifomade -eq 1 ]] || abort "could not make temporary fifo" + +local delprompt +for f in $@; do + local dest="$dirstr/${f:t:r}.$extstr" + + if [[ -e $dest ]]; then + printf "destination file \`$dest\` exists\ndelete it? [y/N]: " + read -q delprompt + print "" + [[ $delprompt = "y" ]] || return 0 + 2>/dev/null rm $dest || abort "could not delete file \`$dest\`" + fi +done + +local totalstr="${#@} file" +[[ $#@ -ne 1 ]] && totalstr="${totalstr}s" +local jobstr=$jobcount +[[ $#@ -lt $jobcount ]] && jobstr=$#@ +[[ $jobstr -gt 1 ]] && jobstr="${jobstr} jobs" || jobstr="${jobstr} job" + +( +{ + local joblist=() + while read -d $'\0' f; do + local dest="$dirstr/${f:t:r}.$extstr" + ( + ffmpeg -loglevel -8 -i $f \ + -vn -sn -c:a $codecstr \ + ${bitratestr/*k/-b:a} $bitratestr \ + ${qstr/[0-9]*/-q:a} $qstr \ + $metastr \ + $dest + echo "done" + ) & + joblist+=$! + done + + local j + for j in $joblist; do + 2>/dev/null wait $j + done +} < $fifoname | { + repeat $jobcount; do + if [[ $#@ -gt 0 ]]; then + printf "%s\0" $@[1] + shift + fi + done + + while read line; do + if [[ $#@ -gt 0 ]]; then + printf "%s\0" $@[1] + shift + else + break + fi + done +} > $fifoname +)& + +pid=$! + +wait-anim $pid "using $jobstr to convert $totalstr to $codecprintstr" + +2>/dev/null rm $fifoname diff --git a/.config/init/funcs/mpd-cover-convert b/.config/init/funcs/mpd-cover-convert index e8359b7..d9d7a2c 100755 --- a/.config/init/funcs/mpd-cover-convert +++ b/.config/init/funcs/mpd-cover-convert @@ -3,10 +3,14 @@ # leaks memory for some reason, so don't run on a clean library # or it'll crash everything -local d +source "$HOME/.config/init/helpers" || exit 1 + +(local file find ~/music -type f -regextype posix-extended -regex ".*cover\.(png|jpg)"\ | while read file; do if [[ ! -f "${file:h}/cover-small.png" ]]; then convert "$file" -resize 250x "${file:h}/cover-small.png" fi -done +done) & + +wait-anim $! "converting" diff --git a/.config/init/funcs/scap b/.config/init/funcs/scap index 85a083f..f87eb93 100755 --- a/.config/init/funcs/scap +++ b/.config/init/funcs/scap @@ -7,8 +7,8 @@ xset q archey3 sleep .2 -if [[ -f '/tmp/cap.mp4' ]]; then - rm '/tmp/cap.mp4' +if [[ -e '/tmp/cap.mp4' ]]; then + rm '/tmp/cap.mp4' || return 1 fi echo 'recording...' -- cgit v1.2.3