#!/bin/bash # [b]rowse - overview of given files or current directory # depends: tput stat bat checkaccess(in my dotfiles) # optdepends: timg, neovim (compressed files), pdftoppm (PDF), mtn (video), audiowaveform # args: files to inspect, any arg starting with dash is passed on to bat # # Supports: # - listing directories # - listing contents of any compressed file with neovim # - visual files are displayed with timg # video thumbnails via mtn, pdf pages from pdftoppm # - text files are displayed through bat # Automatically requests elevation through sudo when needed set -o pipefail test "$1" = "-v" && set -eo xtrace && shift inspect=false # inspect: Show file info without preview test "$1" = "-i" && inspect=true && shift checkperm() { checkaccess -r "$@" || elevate=sudo mime="$(test -n "$shifted" || $elevate file --dereference --mime "$@")" } fileinfo() { tput setaf 6 for arg do case "$arg" in (-*) continue;; esac $elevate file --exclude elf -E "$arg" # TODO do not grep bitrate but extract properly #probe="$($elevate ffprobe "$arg" 2>&1)" #echo $probe | grep -v -e '00:00:00.04' -e 'ansi' && $elevate ffprobe -hide_banner "$arg" 2>&1 | grep "bitrate: ....\? " | sed 's/, start:[^,]\+,/,/' || $elevate stat --format "%U:%G %A %s $( size="$($elevate unzip -l "$arg" 2>/dev/null | tail -1)" && echo "(uncompressed $(echo $size | cut -d' ' -f1 | numfmt --to=iec-i --suffix=B))" ) - birth %.10w mod %.10y" "$arg" | numfmt --field=3 --to=iec-i --padding=6 --suffix=B done tput sgr0 } prefix=/tmp/b mkdir -p "$prefix" declare -a timg timga bat batplain ls for arg; do case "$arg" in (-*) flags="$flags $arg"; continue;; esac checkperm "$arg" if ! $elevate test -e "$arg" then if test -h "$arg" then fileinfo "$arg" else echo "File not found: '$arg'" 1>&2 fi continue fi grid=$(expr $(tput cols) / \( 25 - \( $# / 2 \) \& $# \< 30 \| 5 \)) tmpfile="$prefix/$(basename "$arg")" mkdir -p "$prefix" case "$mime" in (*\ application/pdf\;*) echo Converting "$arg" test -f "$tmpfile-1.ppm" || pdftoppm -forcenum -r 70 "$arg" "$tmpfile" -l $(expr $grid \& \( $grid \> 4 \| $# \> 1 \) \| $grid '*' 2) timg -W --grid=$grid "$tmpfile"*.ppm ;; (*\ application/*document*) # https://ask.libreoffice.org/t/convert-to-command-line-parameter/840/4 echo Converting "$arg" soffice --headless --convert-to png --outdir "$prefix" "$arg" >/dev/null timg+=("${tmpfile%.*}.png") continue;; (*/x-xcf*) echo Converting "$arg" convert -flatten "$arg" png:"$tmpfile" timg+=("$tmpfile") continue;; (*\ video/*) suffix=_thumbs.jpg mtn -i -t -W -r2 -D6 -b 0,6 -c $grid -w $(expr $(tput cols) '*' 20) \ -O "$prefix" -o "$suffix" "$arg" 2>/dev/null timg -W "$prefix/$(basename "${arg%.*}")$suffix" ;; (*\ image/*) timg+=("$arg"); continue;; (*\ inode/directory\;*) ls+=("$arg") test -L "$arg" || continue ;; (*) case "$(file --dereference "$arg")" in (*\ ?udio*) # TODO preconvert aac - |*\ ADTS\ * img="$tmpfile.png" case $TERM in (*-kitty) kitty=true; audioheight=2;; (*) audioheight=5;; esac if which audiowaveform 2>/dev/null >&2 then find "$img" -not -empty 2>/dev/null | grep --quiet . || audiowaveform --quiet --pixels-per-second 2 --height 36 --width 2000 --amplitude-scale auto \ --background-color 000000 --waveform-color 99BBFF --axis-label-color 000000 \ --input-filename "$arg" --output-format png >"$img" && { test "$kitty" && timg -g x$audioheight "$arg" && printf "\\033[${audioheight}A%$(expr $audioheight \* 3)s" timg -g x$audioheight --auto-crop --upscale "$img" } fi timga+=("$arg") ;; (*:\ *compress*|*\ archive*) list="$tmpfile-list.txt" if test $# = 1 then nvim "$arg" else case "$arg" in (*.part);; (*) nvim -es "+2w$list|5,w>>$list" "$arg" batplain+=("$list");; esac; fi ;; (*:\ *database*) highlight "Tables" && sqlite3 "$arg" ".tables";; (*:\ data) ;; (*) bat+=("$arg") timga+=("$arg") continue;; esac ;; esac fileinfo "$arg" done # timg: images # timga: potentially viewable as image if test "$timg"; then # TODO Don't show info on all images for gifs $inspect || $elevate timg $(test "$timga" && echo "-V") --rotate=exif -g $(tput cols)x$(expr $(tput lines) / 2) \ $(test $# -gt 1 && echo "-t0.2 --center $(test $# -lt 20 && echo "--title") --grid=$((grid < $# ? grid : $#))x2") \ "${timg[@]}" "${timga[@]}" 2>/dev/null || true if $inspect || test $# -lt 10; then tput setaf 6 for img in "${timg[@]}" do ident="$(identify -ping -precision 3 -format "%wx%h %b %m %[bit-depth]-bit %[colorspace]" "$img")" printf "%11s %-30s %s\n" "${ident%% *}" "$(basename "$img")" "${ident#* }" done tput sgr0 fi fi pager="${PAGER:-'less -RF'}" # bat: unknown files # batplain: files to print without header if test "$bat" -o "$batplain"; then test "$(bat --version | cut -d. -f2)" -gt 16 && rule=,rule if test $# -gt ${#bat[@]} -a $# -gt ${#batplain[@]} && test -z "$flags" then cut="--line-range :7" batpager="cut -c-$(echo $(tput cols) \* 19 / 10 | cut -d. -f1)" fi batcommand="$elevate bat $cut $flags --pager" batstyle="--style plain$rule" test "$batplain" && $batcommand "${batpager:-$pager}" $batstyle "${batplain[@]}" test "$bat" && if test "$cut" && ! $inspect then case $TERM in (*kitty) declare -a timgtxt for file in "${bat[@]}" do txt="$prefix/$(basename "$file").txt" cp "$file" "$txt" timgtxt+=("$txt") done timg -V --grid="$(expr 4 \& ${#bat[@]} \> 5 \| ${#bat[@]})" --title="%b" --frames=3 "${timgtxt[@]}" #fileinfo "${bat[@]}" ;; (*) for file in "${bat[@]}"; do $batcommand "${batpager:-$pager}" $batstyle "$file" fileinfo "$file" done;; esac else $inspect || $batcommand "${batpager:-$pager}" $batstyle,header$(test $# -gt 1 && echo ",numbers") "${bat[@]}" test $# -lt $(expr $(tput lines) / 3) && fileinfo "${bat[@]}" fi fi if test "$ls" -o $# -le $(echo "$flags" | wc -w); then checkperm . # Alternative: find -exec ls -dl {} + { timeout .6s sh -c " if which exa 2>/dev/null >&2 then exa --color=always --long --group --classify --all --all --sort=changed --reverse $flags $(printf "'%s' " "${ls[@]:-.}") else $elevate ls -l $(test $# -gt ${#ls[@]} && echo '-d') --color=always --human-readable --si --group-directories-first --file-type --dereference-command-line-symlink-to-dir --all $flags $(printf "'%s' " "${ls[@]:-.}") fi " || $elevate ls $(test $# -gt ${#ls[@]} && echo "-d") --color=always --human-readable --si --dereference-command-line --all --sort=none $flags "${ls[@]:-.}" } | $pager --quit-if-one-screen fi