From: luukvbaal <31730729+luukvbaal@users.noreply.github.com> Date: Sat, 13 Mar 2021 11:22:51 +0000 (+0100) Subject: preview-tui improvements (#869) X-Git-Tag: v3.6~7 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=1571ea2b35f4944f8515ae7647181bb206e10113;p=nnn.git preview-tui improvements (#869) * Update preview-tui * trap SIGWINCH * Update preview-tui fix CircleCI test * fix CircleCI... * add pdf/epub forgot to add pdf/epub when adpating my own working script to the upstream version * add USE tests * add office preview allows preview of ods, odt, docx, pptx, etc * remove resize trap, show static gif Removed the faulty resize code and show static gif when USE_GIFPREVIEW is not set. * loop gif playback * refactor imagepreview * scale kitty preview * fix ueberzug_remove regression * add optional atool support * single depth, colorized tree preview also empty pager prompt * atool optional * window resize working * USE_UEBERZUG check * conditional trap I can't fully explain the behavior I'm seeing but this seems to work as its supposed to. * adopt TMPDIR * prevent extra readline after window resize * shellcheck fix * clear preview pane after resize prevents (seemingly random) pager previews from showing after resize * fix stray fifo from subshells * add preview-tui-ext --- diff --git a/plugins/preview-tui b/plugins/preview-tui index b260986b..601b2d4f 100755 --- a/plugins/preview-tui +++ b/plugins/preview-tui @@ -3,6 +3,7 @@ # Description: Terminal based file previewer # # Note: This plugin needs a "NNN_FIFO" to work. See man. +# For a more extended version of this script, including ueberzug support, see preview-tui-ext. # # Dependencies: # - Supports 3 independent methods to preview with: @@ -17,7 +18,7 @@ # - tar # - man # - optional: bat for code syntax highlighting -# - optional: kitty terminal, catimg, viu, or ueberzug for images +# - optional: kitty terminal or catimg for images # - optional: scope.sh file viewer from ranger. # To use: # 1. drop scope.sh executable in $PATH @@ -26,10 +27,6 @@ # To use: # 1. install pistol # 2. set/export $USE_PISTOL as 1 -# - optional: ffmpegthumbnailer for video thumbnails (https://github.com/dirkvdb/ffmpegthumbnailer). -# To use: -# 1. install ffmpegthumbnailer -# 2. set/export USE_VIDEOTHUMB as 1 # # Usage: # You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG, @@ -67,7 +64,6 @@ SPLIT="$SPLIT" # you can set a permanent split here TERMINAL="$TERMINAL" # same goes for the terminal USE_SCOPE="${USE_SCOPE:-0}" USE_PISTOL="${USE_PISTOL:-0}" -USE_VIDEOTHUMB="${USE_VIDEOTHUMB:-0}" PAGER="${PAGER:-less -R}" [ "$PAGER" = "most" ] && PAGER="less -R" @@ -156,13 +152,7 @@ preview_file () { fifo_pager ls --color=always fi elif [ "$encoding" = "binary" ]; then - if [ "${mimetype%%/*}" = "image" ] || [ "${mimetype%%/*}" = "video" ]; then - if [ "${mimetype%%/*}" = "video" ] && [ "$USE_VIDEOTHUMB" -ne 0 ] && exists ffmpegthumbnailer; then - videothumb="/tmp/videothumb.$$.png" - ffmpegthumbnailer -s 512 -i "$1" -o "$videothumb" >/dev/null 2>&1 - set "$videothumb" - trap 'rm "$videothumb"' EXIT - fi + if [ "${mimetype%%/*}" = "image" ] ; then if [ "$TERMINAL" = "kitty" ]; then # Kitty terminal users can use the native image preview method. kitty +kitten icat --silent --transfer-mode=stream --stdin=no \ @@ -171,8 +161,6 @@ preview_file () { catimg "$1" elif exists viu; then viu -t "$1" - elif exists ueberzug; then - preview_ueberzug "$cols" "$lines" "$1" else fifo_pager print_bin_info "$1" fi @@ -195,13 +183,6 @@ preview_file () { fi } -preview_ueberzug() { - { - printf '{"action": "add", "identifier": "nnn_ueberzug", "x": 0, "y": 0, "width": "%s", "height": "%s", "path": "%s"}\n' "$1" "$2" "$3" - read -r - } | ueberzug layer --parser json -} - if [ "$PREVIEW_MODE" ] ; then if [ ! -r "$NNN_FIFO" ] ; then echo "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')" >&2 diff --git a/plugins/preview-tui-ext b/plugins/preview-tui-ext new file mode 100755 index 00000000..bfb04af5 --- /dev/null +++ b/plugins/preview-tui-ext @@ -0,0 +1,370 @@ +#!/usr/bin/env sh + +# Description: Terminal based file previewer +# +# Note: This plugin needs a "NNN_FIFO" to work. See man. +# +# Dependencies: +# - Supports 3 independent methods to preview with: +# - tmux (>=3.0), or +# - kitty with allow_remote_control on, or +# - $TERMINAL set to a terminal (it's xterm by default). +# - less or $PAGER +# - tree or exa or ls +# - mediainfo or file +# - mktemp +# - unzip +# - tar +# - man +# - optional: atool for additional archive preview +# - optional: bat for code syntax highlighting +# - optional: kitty terminal, catimg, viu, or ueberzug for images +# - optional: scope.sh file viewer from ranger. +# To use: +# 1. drop scope.sh executable in $PATH +# 2. set/export $USE_SCOPE as 1 +# - optional: pistol file viewer (https://github.com/doronbehar/pistol). +# To use: +# 1. install pistol +# 2. set/export $USE_PISTOL as 1 +# - optional: ffmpegthumbnailer for video thumbnails (https://github.com/dirkvdb/ffmpegthumbnailer). +# To use: +# 1. install ffmpegthumbnailer +# 2. set/export USE_VIDEOPREVIEW as 1 +# - optional: convert(ImageMagick) for playing gif preview +# To use: +# 1. install ImageMagick +# 2. set/export USE_GIFPREVIEW as 1 +# 3. set/export LOOP_GIF as 1 if you want to loop gif preview +# - optional: ffmpeg for audio thumbnails +# To use: +# 1. install ffmpeg +# 2. set/export USE_AUDIOPREVIEW as 1 +# - optional: pdftoppm(poppler) for pdf thumbnails +# To use: +# 1. install poppler +# 2. set/export USE_PDFPREVIEW as 1 +# - optional: gnome-epub-thumbnailer for epub thumbnails (https://gitlab.gnome.org/GNOME/gnome-epub-thumbnailer) +# To use: +# 1. install gnome-epub-thumbnailer +# 2. set/export USE_EPUBPREVIEW as 1 +# - optional: fontpreview for font preview (https://github.com/sdushantha/fontpreview) +# To use: +# 1. install fontpreview +# 2. set/export USE_FONTPREVIEW as 1 +# - optional: libreoffce for opendocument/officedocument preview +# To use: +# 1. install libreoffice +# 2. set/export USE_OFFICEPREVIEW as 1 +# +# Usage: +# You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG, +# then start `nnn`: +# +# $ nnn -a +# +# or +# +# $ NNN_FIFO=/tmp/nnn.fifo nnn +# +# Then in `nnn`, launch the `preview-tui` plugin. +# +# If you provide the same NNN_FIFO to all nnn instances, there will be a +# single common preview window. If you provide different FIFO path (e.g. +# with -a), they will be independent. +# +# The previews will be shown in a tmux split. If that isn't possible, it +# will try to use a kitty terminal split. And as a final fallback, a +# different terminal window will be used ($TERMINAL). +# +# Tmux and kitty users can configure $SPLIT to either "h" or "v" to set a +# 'h'orizontal split or a 'v'ertical split (as in, the line that splits the +# windows will be horizontal or vertical). +# +# Kitty users need `allow_remote_control` set to `yes`. To customize the +# window split, `enabled_layouts` has to be set to `all` or `splits` (the +# former is the default value). This terminal is also able to show images +# without extra dependencies. +# +# Shell: POSIX compliant +# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero + +SPLIT="$SPLIT" # you can set a permanent split here +TERMINAL="$TERMINAL" # same goes for the terminal +USE_SCOPE="${USE_SCOPE:-0}" +USE_PISTOL="${USE_PISTOL:-0}" +USE_UEBERZUG="${USE_UEBERZUG:-0}" +USE_IMAGEPREVIEW="${USE_IMAGEPREVIEW:-0}" +USE_VIDEOPREVIEW="${USE_VIDEOPREVIEW:-0}" +USE_AUDIOPREVIEW="${USE_AUDIOPREVIEW:-0}" +USE_FONTPREVIEW="${USE_FONTPREVIEW:-0}" +USE_EPUBPREVIEW="${USE_EPUBPREVIEW:-0}" +USE_PDFPREVIEW="${USE_PDFPREVIEW:-0}" +USE_OFFICEPREVIEW="${USE_OFFICEPREVIEW:-0}" +USE_GIFPREVIEW="${USE_GIFPREVIEW:-0}" +LOOP_GIFS="${LOOP_GIFS:-0}" +PAGER="${PAGER:-less -P?n -R}" +ARCHIVES="$(echo "$NNN_ARCHIVE" | sed 's/.*(\(.*\)).*/\1/;s/|/ /g')" +TMPDIR="${TMPDIR:-/tmp}" + +[ "$PAGER" = "most" ] && PAGER="less -R" + +if [ -e "${TMUX%%,*}" ] && tmux -V | grep -q '[ -][3456789]\.'; then + TERMINAL=tmux +elif [ -n "$KITTY_WINDOW_ID" ] && kitty @ ls >/dev/null 2>&1; then + TERMINAL=kitty +else + TERMINAL="${TERMINAL:-xterm}" +fi + +if [ -z "$SPLIT" ] && [ $(($(tput lines) * 2)) -gt "$(tput cols)" ]; then + SPLIT='h' +elif [ "$SPLIT" != 'h' ]; then + SPLIT='v' +fi + +exists() { + which "$1" >/dev/null 2>&1 +} + +fifo_pager() { + cmd="$1" + shift + + # We use a FIFO to access $PAGER PID in jobs control + tmpfifopath="$TMPDIR/nnn-preview-tui-fifo.$$" + mkfifo "$tmpfifopath" || return + + $PAGER < "$tmpfifopath" & + pagerpid="$!" + + ( + exec > "$tmpfifopath" + "$cmd" "$@" & + ) + + rm "$tmpfifopath" +} + +# Binary file: show file info inside the pager +print_bin_info() { + printf -- "-------- \033[1;31mBinary file\033[0m --------\n" + if exists mediainfo; then + mediainfo "$1" 2>/dev/null + else + file -b "$1" + fi +} + +preview_file () { + kill %- %+ 2>/dev/null && wait %- %+ 2>/dev/null + clear + + # Trying to use pistol if it's available. + if [ "$USE_PISTOL" -ne 0 ] && exists pistol; then + fifo_pager pistol "$1" + return + fi + + # Trying to use scope.sh if it's available. + if [ "$USE_SCOPE" -ne 0 ] && exists scope.sh; then + fifo_pager scope.sh "$1" "$cols" "$lines" "$(mktemp -d)" \ + "True" 2>/dev/null + return + fi + + # Detecting the exact type of the file: the encoding, mime type, and + # extension in lowercase. + encoding="$(file -Lb --mime-encoding -- "$1")" + mimetype="$(file -Lb --mime-type -- "$1")" + ext="${1##*.}" + if [ -n "$ext" ]; then + ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" + fi + lines=$(($(tput lines)-1)) + cols=$(tput cols) + + # Otherwise, falling back to the defaults. + if [ -d "$1" ]; then + cd "$1" || return + if exists tree; then + fifo_pager tree -L 1 --dirsfirst -F -C + elif exists exa; then + fifo_pager exa -G --colour=always 2>/dev/null + else + fifo_pager ls --color=always + fi + elif [ "$encoding" = "binary" ]; then + if [ "$USE_GIFPREVIEW" -ne 0 ] && [ "$ext" = "gif" ]; then + generate_preview "$cols" "$lines" "$1" "gif" + elif [ "$USE_IMAGEPREVIEW" -ne 0 ] && [ "${mimetype%%/*}" = "image" ]; then + display_preview "$cols" "$lines" "$1" + elif [ "$USE_AUDIOPREVIEW" -ne 0 ] && [ "${mimetype%%/*}" = "audio" ]; then + generate_preview "$cols" "$lines" "$1" "audio" + elif [ "$USE_VIDEOPREVIEW" -ne 0 ] && [ "${mimetype%%/*}" = "video" ]; then + generate_preview "$cols" "$lines" "$1" "video" + elif [ "$USE_PDFPREVIEW" -ne 0 ] && [ "$ext" = "pdf" ]; then + generate_preview "$cols" "$lines" "$1" "pdf" + elif [ "$USE_EPUBPREVIEW" -ne 0 ] && [ "$ext" = "epub" ]; then + generate_preview "$cols" "$lines" "$1" "epub" + elif [ "$USE_FONTPREVIEW" -ne 0 ] && [ "${mimetype%%/*}" = "font" ]; then + generate_preview "$cols" "$lines" "$1" "font" + elif [ "${mimetype#*office}" != "$mimetype" ] || [ "${mimetype#*document}" != "$mimetype" ] && [ "$USE_OFFICEPREVIEW" -ne 0 ]; then + generate_preview "$cols" "$lines" "$1" "office" + elif [ "${ARCHIVES#*$ext}" != "$ARCHIVES" ] && exists atool; then + fifo_pager atool -l "$1" + elif [ "$mimetype" = "application/zip" ]; then + fifo_pager unzip -l "$1" + elif [ "$ext" = "gz" ] || [ "$ext" = "bz2" ]; then + fifo_pager tar -tvf "$1" + else + fifo_pager print_bin_info "$1" + fi + elif [ "$mimetype" = "text/troff" ]; then + fifo_pager man -Pcat -l "$1" + else + if exists bat; then + fifo_pager bat --terminal-width="$cols" --paging=never --decorations=always --color=always \ + "$1" 2>/dev/null + else + $PAGER "$1" & + fi + fi +} + +generate_preview() { + if [ ! -f "$TMPDIR/$3.png" ]; then + fifo_pager print_bin_info "$3" + mkdir -p "$TMPDIR/${3%/*}" + case $4 in + audio) ffmpeg -i "$3" "$TMPDIR/$3.png" -y >/dev/null 2>&1 ;; + epub) gnome-epub-thumbnailer "$3" "$TMPDIR/$3.png" >/dev/null 2>&1 ;; + font) fontpreview -i "$3" -o "$TMPDIR/$3.png" >/dev/null 2>&1 ;; + gif) if [ "$USE_UEBERZUG" -ne 0 ] || [ "$TERMINAL" = "kitty" ] && [ "$USE_GIFPREVIEW" -ne 0 ]; then + if [ ! -d "$TMPDIR/$3" ]; then + mkdir -p "$TMPDIR/$3" + convert -coalesce "$3" "$TMPDIR/$3/${3##*/}.png" + fi + while true; do + for frame in $(find "$TMPDIR/$3"/*.png | sort -V); do + display_preview "$1" "$2" "$frame" + sleep 0.1 + done + [ "$LOOP_GIFS" -eq 0 ] && return + done & + gifpid="$!" + return + else + display_preview "$1" "$2" "$3" + return + fi ;; + office) libreoffice --convert-to png "$3" --outdir "$TMPDIR/${3%/*}" > /dev/null 2>&1 + filename="$(echo "${3##*/}" | cut -d. -f1)" + mv "$TMPDIR/${3%/*}/$filename.png" "$TMPDIR/$3.png" ;; + pdf) pdftoppm -png -f 1 -singlefile "$3" "$TMPDIR/$3" >/dev/null 2>&1 ;; + video) ffmpegthumbnailer -i "$3" -o "$TMPDIR/$3.png" -s 0 -q 10 >/dev/null 2>&1 ;; + esac + kill "$pagerpid" + fi + display_preview "$1" "$2" "$TMPDIR/$3.png" +} + +display_preview() { + if [ "$USE_UEBERZUG" -ne 0 ] && exists ueberzug; then + ueberzug_layer "$1" "$2" "$3" + elif [ "$TERMINAL" = "kitty" ]; then + # Kitty terminal users can use the native image preview method. + kitty +kitten icat --silent --place "$1"x"$2"@0x0 --transfer-mode=stream --stdin=no \ + "$3" + elif exists catimg; then + catimg "$3" & + gifpid="$!" + elif exists viu; then + viu -t "$3" & + gifpid="$!" + fi +} + +ueberzug_layer() { + printf '{"action": "add", "identifier": "nnn_ueberzug", "x": 0, "y": 0, "width": "%s", "height": "%s", "path": "%s"}\n' "$1" "$2" "$3" > "$FIFO_UEBERZUG" +} + +ueberzug_remove() { + printf '{"action": "remove", "identifier": "nnn_ueberzug"}\n' > "$FIFO_UEBERZUG" +} + +ueberzug_refresh() { + clear + pkill -P "$$" + pkill -f -n preview-tui + echo > "$NNN_FIFO" + tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & + preview_fifo & + wait +} +[ "$USE_UEBERZUG" -ne 0 ] && trap 'ueberzug_refresh' WINCH + +preview_fifo() { + # use cat instead of 'exec <' to avoid issues with dash shell + # shellcheck disable=SC2002 + cat "$NNN_FIFO" |\ + while read -r selection ; do + [ "$gifpid" -ne 0 ] && kill "$gifpid" + [ "$USE_UEBERZUG" -ne 0 ] && ueberzug_remove + preview_file "$selection" + done + [ "$USE_UEBERZUG" -ne 0 ] && rm "$FIFO_UEBERZUG" +} + + +if [ "$PREVIEW_MODE" ]; then + if [ ! -r "$NNN_FIFO" ]; then + echo "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')" >&2 + read -r + exit 1 + fi + + if [ "$USE_UEBERZUG" -ne 0 ]; then + FIFO_UEBERZUG="$TMPDIR/nnn-ueberzug-fifo.$$" + mkfifo "$FIFO_UEBERZUG" + fi + + preview_file "$1" + if [ "$USE_UEBERZUG" -ne 0 ]; then + tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & + fi + + preview_fifo & + wait + + # Restoring the previous layout for kitty users. This will only work for + # kitty >= 0.18.0. + if [ "$TERMINAL" = "kitty" ]; then + kitty @ last-used-layout --no-response >/dev/null 2>&1 + fi + + exit 0 +fi + +if [ "$TERMINAL" = "tmux" ]; then + # tmux splits are inverted + if [ "$SPLIT" = "v" ]; then SPLIT="h"; else SPLIT="v"; fi + + tmux split-window -e "NNN_FIFO=$NNN_FIFO" -e "PREVIEW_MODE=1" -d"$SPLIT" "$0" "$1" +elif [ "$TERMINAL" = "kitty" ]; then + # Setting the layout for the new window. It will be restored after the + # script ends. + kitty @ goto-layout splits >/dev/null + + # Trying to use kitty's integrated window management as the split window. + # All environmental variables that will be used in the new window must + # be explicitly passed. + kitty @ launch --no-response --title "nnn preview" --keep-focus \ + --cwd "$PWD" --env "PATH=$PATH" --env "NNN_FIFO=$NNN_FIFO" \ + --env "PREVIEW_MODE=1" --env "PAGER=$PAGER" \ + --env "USE_SCOPE=$USE_SCOPE" --env "SPLIT=$SPLIT" \ + --env "USE_PISTOL=$USE_PISTOL" \ + --location "${SPLIT}split" "$0" "$1" >/dev/null +else + PREVIEW_MODE=1 $TERMINAL -e "$0" "$1" & +fi