]> Sergey Matveev's repositories - nnn.git/commitdiff
preview-tui improvements (#869)
authorluukvbaal <31730729+luukvbaal@users.noreply.github.com>
Sat, 13 Mar 2021 11:22:51 +0000 (12:22 +0100)
committerGitHub <noreply@github.com>
Sat, 13 Mar 2021 11:22:51 +0000 (16:52 +0530)
* 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

plugins/preview-tui
plugins/preview-tui-ext [new file with mode: 0755]

index b260986bfb296aab2694e2f30d371ac96cd23812..601b2d4fac46986301fd4a40193321db59c9ccb0 100755 (executable)
@@ -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
 #                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 (executable)
index 0000000..bfb04af
--- /dev/null
@@ -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