aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.config/init/funcreqs/audio-concat2
-rw-r--r--.config/init/funcreqs/audio-convert2
-rwxr-xr-x.config/init/funcs/audio-concat127
-rwxr-xr-x.config/init/funcs/audio-convert233
-rwxr-xr-x.config/init/funcs/mpd-cover-convert8
-rwxr-xr-x.config/init/funcs/scap4
-rw-r--r--.config/init/helpers77
-rw-r--r--.config/ranger/rifle.conf5
-rw-r--r--README.md9
9 files changed, 458 insertions, 9 deletions
diff --git a/.config/init/funcreqs/audio-concat b/.config/init/funcreqs/audio-concat
new file mode 100644
index 0000000..b3cc909
--- /dev/null
+++ b/.config/init/funcreqs/audio-concat
@@ -0,0 +1,2 @@
+func_init_prereqs=(ffmpeg)
+func_init_checks=()
diff --git a/.config/init/funcreqs/audio-convert b/.config/init/funcreqs/audio-convert
new file mode 100644
index 0000000..b3cc909
--- /dev/null
+++ b/.config/init/funcreqs/audio-convert
@@ -0,0 +1,2 @@
+func_init_prereqs=(ffmpeg)
+func_init_checks=()
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...] <infile>..."
+ echo "Concatenate multiple audio files into one"
+ echo ""
+ echo " \e[1mdescription opt longform arg default\e[0m"
+ echo " specify destination file -o --outfile <file> out.ogg"
+ echo " specify output bitrate -b --bitrate <int>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...] <infile>..."
+ echo "Convert multiple audio files to a new format"
+ echo ""
+ echo " \e[1mdescription opt longform arg default\e[0m"
+ echo " output codec -c --codec <codec> opus"
+ echo " output bitrate -b --bitrate <int>k (opus:35k, vorbis:50k, mp3:65k, flac:N/A)"
+ echo " output quality -q --quality <int> N/A"
+ echo " output directory -d --dest <dir> ."
+ echo " copy metadata -m --metadata <bool> false"
+ echo " background job count -j --jobs <int> 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...'
diff --git a/.config/init/helpers b/.config/init/helpers
new file mode 100644
index 0000000..69963cb
--- /dev/null
+++ b/.config/init/helpers
@@ -0,0 +1,77 @@
+print-error() {
+ 1>&2 echo "\e[1;31merror:\e[0m $1"
+}
+
+abort() {
+ print-error $1
+ exit 1
+}
+
+wait-anim() {
+ [[ $#@ -ne 0 ]] && [[ $#@ -lt 4 ]] || return 1
+
+ local pidpat='^[1-9][0-9]*$'
+ local colourpat='^(black|light_black|red|light_red|green|light_green|yellow|light_yellow|blue|light_blue|magenta|light_magenta|cyan|light_cyan|white|light_white)$'
+
+ [[ $1 =~ $pidpat ]] || return 1
+ if [[ ! -z $3 ]]; then
+ [[ $3 =~ $colourpat ]] || return 1
+ fi
+
+ local message
+ local colour
+
+ [[ -z $2 ]] && message='waiting...' || message=$2
+ [[ -z $3 ]] && colour='yellow' || colour=$3
+
+ case $colour in
+ 'black') colour='\e[30m' ;;
+ 'light_black') colour='\e[1;30m' ;;
+ 'red') colour='\e[31m' ;;
+ 'light_red') colour='\e[1;31m' ;;
+ 'green') colour='\e[32m' ;;
+ 'light_green') colour='\e[1;32m' ;;
+ 'yellow') colour='\e[33m' ;;
+ 'light_yellow') colour='\e[1;33m' ;;
+ 'blue') colour='\e[34m' ;;
+ 'light_blue') colour='\e[1;34m' ;;
+ 'magenta') colour='\e[35m' ;;
+ 'light_magenta') colour='\e[1;35m' ;;
+ 'cyan') colour='\e[36m' ;;
+ 'light_cyan') colour='\e[1;36m' ;;
+ 'white') colour='\e[37m' ;;
+ 'light_white') colour='\e[1;37m' ;;
+ esac
+
+ local curframe
+ local frames
+
+ case $(($RANDOM % 7)) in
+ 0) frames=('▁' '▂' '▃' '▄' '▅' '▆' '▇' '█' '▇' '▆' '▅' '▄' '▃' '▂') ;;
+ 1) frames=(' ' '▖' '▚' '▝' ' ' '▘' '▞' '▗') ;;
+ 2) frames=('╀' '╂' '╁' '┼' '┽' '┿' '┾' '┼' '╁' '╂' '╀' '┼' '┾' '┿' '┽' '┼') ;;
+ 3) frames=('─' '└' '│' '┌' '─' '┐' '│' '┘') ;;
+ 4) frames=('.' '。' '×' ':' '*' '.' '°' '♪' '×' '♫' '°' '♡') ;;
+ 5) frames=(' ' ' ' '░' '░' '▒' '▒' '▓' '▓' '█' '█' '▓' '▓' '▒' '▒' '░' '░') ;;
+ 6) frames=('O' 'o' '.' 'o') ;;
+ esac
+
+ (while 2>/dev/null kill -0 $1; do
+ if [[ -z $curframe ]]; then
+ curframe=0
+ printf "\e[s"
+ else
+ printf "\e[u\e[K"
+ fi
+ printf "|\e[1;32m${frames[$(($curframe + 1))]}\e[0m| $colour$message\e[0m"
+ curframe=$(( ($curframe + 1) % $#frames ))
+ sleep .2
+ done) &
+
+ # trap "echo -n '\e[G\e[J\e[s'" SIGWINCH
+
+ wait $1
+ local r=$?
+ printf "\e[u\e[K"
+ return $r
+}
diff --git a/.config/ranger/rifle.conf b/.config/ranger/rifle.conf
index 9f1f981..7000ba6 100644
--- a/.config/ranger/rifle.conf
+++ b/.config/ranger/rifle.conf
@@ -47,7 +47,6 @@
# only running the current file even if you have marked multiple files.
# music conversion and import
-directory, has flac2all = flac2all vorbis "$1" -v quality=10 -o "$1/converted"
directory, has beet = beet imp -t "$1"
# view man pages
@@ -80,6 +79,10 @@ ext php = php -- "$1"
mime ^audio, terminal, has mpv = mpv --no-video -- "$@"
mime ^audio, !terminal, has mpv, X, flag f = mpv --force-window=yes -- "$@"
+mime ^audio, terminal, has audio-convert = audio-convert -d converted -c vorbis -q 10 "$@"
+mime ^audio, terminal, has audio-convert = audio-convert -d converted -c opus "$@"
+mime ^audio, terminal, has audio-concat = audio-concat "$@"
+
mime ^video, has mpv, X, flag f = mpv -- "$@"
mime ^video, has mpv, terminal, !X = mpv --vo=tct -- "$@"
diff --git a/README.md b/README.md
index c3df76f..e12008b 100644
--- a/README.md
+++ b/README.md
@@ -19,12 +19,13 @@ folders "[funcs](.config/init/funcs/)" and
"[funcreqs](.config/init/funcreqs/)", which, respectively, contain executable
scripts and their prerequisite commands and arbitrary check commands. the
latter prerequisites are tested from [.zprofile](.zprofile) at login and, if
-passed, the functions are symlinked into /tmp/funcs, which is included in
-$PATH. thus, this system allows for configs which automatically adapt to the
+passed, the functions are symlinked into /tmp/funcs, which is appended to
+`$PATH`. thus, this system allows for configs which automatically adapt to the
host environment, enabling only what functionality is compatible. so far i've
been the only user on systems using this config, but will probably make the dir
-these are written into configurable as well if that ever happens (not safe at
-all to stick the things you're running in tmp on a multi-user system!).
+these are written into configurable as well if that ever changes (since
+otherwise that's a pretty big security flaw, with anyone who can write to tmp
+able to add commands to the shell).
## Current Utilities