preview-tui (22663B)
1 #!/usr/bin/env bash 2 3 # Description: Terminal based file previewer 4 # 5 # Note: This plugin needs a "NNN_FIFO" to work. See man. 6 # 7 # Dependencies: 8 # - Supports 6 independent methods to preview with: 9 # - tmux (>=3.0), or 10 # - kitty with allow_remote_control and listen_on set in kitty.conf, or 11 # - wezterm (https://wezfurlong.org/wezterm), or 12 # - QuickLook on WSL (https://github.com/QL-Win/QuickLook), or 13 # - Windows Terminal (https://github.com/Microsoft/Terminal | https://aka.ms/terminal) with WSL, or 14 # - $NNN_TERMINAL set to a terminal (it's xterm by default). 15 # - less or $NNN_PAGER 16 # - tree or exa/eza or (GNU) ls 17 # - mediainfo or file 18 # - mktemp 19 # - unzip 20 # - tar 21 # - man 22 # - optional: bsdtar or atool for additional archive preview 23 # - optional: bat for code syntax highlighting 24 # - optional: ueberzug, kitty terminal, wezterm terminal, img2sixel, viu, catimg or chafa for images 25 # - optional: convert(ImageMagick) for playing gif preview (mandatory for kitty image previews) 26 # - optional: mpv for gif and video 27 # Also requires a terminal supporting the sixel (https://www.arewesixelyet.com/) 28 # or kitty (https://sw.kovidgoyal.net/kitty/graphics-protocol) video_output backends. 29 # Requires tmux compiled with `./configure --enable-sixel` if used. 30 # - optional: ffmpegthumbnailer for video thumbnails (https://github.com/dirkvdb/ffmpegthumbnailer) 31 # - optional: ffmpeg for audio thumbnails 32 # - optional: libreoffce for opendocument/officedocument preview 33 # - optional: pdftoppm(poppler) for pdf thumbnails 34 # - optional: gnome-epub-thumbnailer for epub thumbnails (https://gitlab.gnome.org/GNOME/gnome-epub-thumbnailer) 35 # - optional: fontpreview for font preview (https://github.com/sdushantha/fontpreview) 36 # - optional: djvulibre for djvu 37 # - optional: glow or lowdown for markdown 38 # - optional: w3m or lynx or elinks for html 39 # - optional: set/export NNN_ICONLOOKUP as 1 to enable file icons in front of directory previews with .iconlookup 40 # Icons and colors are configurable in .iconlookup 41 # - optional: scope.sh file viewer from ranger. 42 # 1. drop scope.sh executable in $PATH 43 # 2. set/export $NNN_SCOPE as 1 44 # - optional: pistol file viewer (https://github.com/doronbehar/pistol). 45 # 1. install pistol 46 # 2. set/export $NNN_PISTOL as 1 47 # - optional: librsvg for rsvg-convert 48 # 49 # Usage: 50 # You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG, 51 # then start `nnn`: 52 # 53 # $ nnn -a 54 # 55 # or 56 # 57 # $ NNN_FIFO=/tmp/nnn.fifo nnn 58 # 59 # Then launch the `preview-tui` plugin in `nnn`. 60 # 61 # If you provide the same NNN_FIFO to all nnn instances, there will be a 62 # single common preview window. If you provide different FIFO path (e.g. 63 # with -a), they will be independent. 64 # 65 # The previews will be shown in a tmux split. If that isn't possible, it 66 # will try to use a kitty terminal split. And as a final fallback, a 67 # different terminal window will be used ($NNN_TERMINAL). 68 # 69 # Kitty users need something similar to the following in their kitty.conf: 70 # - `allow_remote_control yes` 71 # - `listen_on unix:$TMPDIR/kitty` 72 # - `enabled_layouts splits` (optional) 73 # With ImageMagick installed, this terminal can use the icat kitten to display images. 74 # Refer to kitty documentation for further details. 75 # 76 # Users with both tmux and kitty can leverage image previews inside tmux with kitty's icat kitten 77 # - setup kitty as stated above 78 # - tmux >= v3.3a required 79 # - add the following to your tmux.conf: 80 # - `set -g allow-passthrough on` 81 # 82 # Wezterm should work out of the box. If `NNN_PREVIEWIMGPROG` is not specified it will use 83 # built in iTerm2 image protocol. 84 # 85 # Note that GNU ls is used for its `--group-directories-first` flag. 86 # On MacOS this may be installed with `brew install coreutils`, or the flag can be removed. 87 # iTerm2 users are recommended to use viu to view images without getting pixelated. 88 # 89 # Windows Terminal users can set "Profile termination behavior" under "Profile > Advanced" settings 90 # to automatically close pane on quit when exit code is 0. 91 # 92 # When specifying a different terminal, additional arguments are supported. In particular, you can 93 # append a specific title to the terminal and set it to "nofocus" in your WM config. 94 # E.g for alacritty and i3, you can set $NNN_TERMINAL to 'alacritty --title preview-tui' and add 95 # 'no_focus [title="preview-tui"]' to your i3 config file. 96 # 97 # Shell: Bash (for environment manipulation through arrays) 98 # Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz, @flipflop133 99 100 # Configurable environment variables: 101 NNN_SPLIT=${NNN_SPLIT:-} # permanent split direction 102 NNN_TERMINAL=${NNN_TERMINAL:-} # external terminal to be used 103 NNN_SPLITSIZE=${NNN_SPLITSIZE:-50} # previewer split size percentage 104 TMPDIR=${TMPDIR:-/tmp} # location of temporary files 105 ENVVARS=( 106 "NNN_SCOPE=${NNN_SCOPE:-0}" # use scope 107 "NNN_PISTOL=${NNN_PISTOL:-0}" # use pistol 108 "NNN_ICONLOOKUP=${NNN_ICONLOOKUP:-0}" # use .iconlookup 109 "NNN_PAGER=${NNN_PAGER:-less -P?n -R -C}" # pager options 110 "NNN_BATTHEME=${NNN_BATTHEME:-ansi}" # bat theme 111 "NNN_BATSTYLE=${NNN_BATSTYLE:-full}" # bat style 112 "NNN_PREVIEWWIDTH=${NNN_PREVIEWWIDTH:-1920}" # width of generated preview images 113 "NNN_PREVIEWHEIGHT=${NNN_PREVIEWHEIGHT:-1080}" # height of generated preview images 114 "NNN_PREVIEWDIR=${NNN_PREVIEWDIR:-$TMPDIR/nnn/previews}" # location of generated preview images 115 "NNN_PREVIEWIMGPROG=${NNN_PREVIEWIMGPROG:-}" # program used to preview images 116 "NNN_PREVIEWVIDEO=${NNN_PREVIEWVIDEO:-}" # mpv backend used to preview video 117 ) 118 # Non-configurable environment variables 119 NNN_PARENT=${NNN_FIFO#*.} 120 [ "$NNN_PARENT" -eq "$NNN_PARENT" ] 2>/dev/null || NNN_PARENT="" # Make empty if non-numeric 121 ENVVARS+=( 122 "PWD=$PWD" 123 "PATH=$PATH" 124 "NNN_FIFO=$NNN_FIFO" 125 "FIFOPID=$TMPDIR/nnn-preview-tui-fifopid.$NNN_PARENT" 126 "FIFOPATH=$TMPDIR/nnn-preview-tui-fifo.$NNN_PARENT" 127 "PREVIEWPID=$TMPDIR/nnn-preview-tui-previewpid.$NNN_PARENT" 128 "CURSEL=$TMPDIR/nnn-preview-tui-selection.$NNN_PARENT" 129 "FIFO_UEBERZUG=$TMPDIR/nnn-preview-tui-ueberzug-fifo.$NNN_PARENT" 130 "POSOFFSET=$TMPDIR/nnn-preview-tui-posoffset" 131 ) 132 133 trap '' PIPE 134 exists() { type "$1" >/dev/null 2>&1 ;} 135 pkill() { command pkill "$@" >/dev/null 2>&1 ;} 136 prompt() { clear; printf "%b" "$@"; cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg" ;} 137 pidkill() { 138 if [ -f "$1" ]; then 139 PID="$(cat "$1" 2>/dev/null)" || return 1 140 kill "$PID" >/dev/null 2>&1 141 RET=$? 142 wait "$PID" 2>/dev/null 143 return $RET 144 fi 145 return 1 146 } 147 148 start_preview() { 149 if [ -e "${TMUX%%,*}" ] && tmux -V | grep -q '[ -][3456789]\.'; then 150 NNN_TERMINAL=tmux 151 exists mpv && tmux display -p '#{client_termfeatures}' | grep -q 'sixel' && ENVVARS+=("NNN_PREVIEWVIDEO=sixel") 152 elif [ -n "$KITTY_LISTEN_ON" ]; then 153 NNN_TERMINAL=kitty 154 exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=kitty") 155 elif [ -n "$WEZTERM_PANE" ]; then 156 NNN_TERMINAL=wezterm 157 exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=kitty") 158 elif [ -z "$NNN_TERMINAL" ] && [ "$TERM_PROGRAM" = "iTerm.app" ]; then 159 NNN_TERMINAL=iterm 160 exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=sixel") 161 elif [ -n "$WT_SESSION" ]; then 162 NNN_TERMINAL=winterm 163 else 164 NNN_TERMINAL="${NNN_TERMINAL:-xterm}" 165 fi 166 167 if [ -z "$NNN_SPLIT" ] && [ $(($(tput lines) * 2)) -gt "$(tput cols)" ]; then 168 NNN_SPLIT='h' 169 elif [ "$NNN_SPLIT" != 'h' ]; then 170 NNN_SPLIT='v' 171 fi 172 173 ENVVARS+=("NNN_TERMINAL=$NNN_TERMINAL" "NNN_SPLIT=$NNN_SPLIT" "QLPATH=$2" "PREVIEW_MODE=1") 174 case "$NNN_TERMINAL" in 175 iterm|winterm) # has to run in separate shell command: escape 176 ENVVARS=("${ENVVARS[@]/#/\\\"}") 177 ENVVARS=("${ENVVARS[@]/%/\\\"}") 178 command="$SHELL -c 'env ${ENVVARS[*]} \\\"$0\\\" \\\"$1\\\"'" ;; 179 esac 180 181 case "$NNN_TERMINAL" in 182 tmux) # tmux splits are inverted 183 ENVVARS=("${ENVVARS[@]/#/-e}") 184 if [ "$NNN_SPLIT" = "v" ]; then split="h"; else split="v"; fi 185 tmux split-window -l"$NNN_SPLITSIZE"% "${ENVVARS[@]}" -d"$split" -p"$NNN_SPLITSIZE" "$0" "$1" ;; 186 kitty) # Setting the layout for the new window. It will be restored after the script ends. 187 ENVVARS=("${ENVVARS[@]/#/--env=}") 188 kitty @ goto-layout splits 189 # Trying to use kitty's integrated window management as the split window. 190 kitty @ launch --no-response --title "preview-tui" --keep-focus \ 191 --cwd "$PWD" "${ENVVARS[@]}" --location "${NNN_SPLIT}split" "$0" "$1" ;; 192 wezterm) 193 export "${ENVVARS[@]}" 194 if [ "$NNN_SPLIT" = "v" ]; then split="--horizontal"; else split="--bottom"; fi 195 wezterm cli split-pane --cwd "$PWD" $split --percent "$NNN_SPLITSIZE" "$0" "$1" >/dev/null 196 wezterm cli activate-pane-direction Prev ;; 197 iterm) 198 if [ "$NNN_SPLIT" = "h" ]; then split="horizontally"; else split="vertically"; fi 199 osascript <<-EOF 200 tell application "iTerm" 201 tell current session of current window 202 split $split with default profile command "$command" 203 end tell 204 end tell 205 EOF 206 ;; 207 winterm) 208 if [ "$NNN_SPLIT" = "h" ]; then split="H"; else split="V"; fi 209 wt -w 0 sp -$split -s"0.$NNN_SPLITSIZE" "$command" \; -w 0 mf previous 2>/dev/null ;; 210 *) if [ -n "$2" ]; then 211 env "${ENVVARS[@]}" QUICKLOOK=1 "$0" "$1" & 212 else 213 # shellcheck disable=SC2086 # (allow arguments) 214 env "${ENVVARS[@]}" $NNN_TERMINAL -e "$0" "$1" & 215 fi ;; 216 esac 217 } 218 219 toggle_preview() { 220 export "${ENVVARS[@]}" 221 if exists QuickLook.exe; then 222 QLPATH="QuickLook.exe" 223 elif exists Bridge.exe; then 224 QLPATH="Bridge.exe" 225 fi 226 if pidkill "$FIFOPID"; then 227 [ -p "$NNN_PPIPE" ] && printf "0" > "$NNN_PPIPE" 228 pidkill "$PREVIEWPID" 229 pkill -f "tail --follow $FIFO_UEBERZUG" 230 if [ -n "$QLPATH" ] && stat "$1"; then 231 f="$(wslpath -w "$1")" && "$QLPATH" "$f" & 232 fi 233 else 234 [ -p "$NNN_PPIPE" ] && printf "1" > "$NNN_PPIPE" 235 start_preview "$1" "$QLPATH" 236 fi 237 } 238 239 fifo_pager() { 240 cmd="$1" 241 shift 242 243 # We use a FIFO to access $NNN_PAGER PID in jobs control 244 mkfifo "$FIFOPATH" || return 245 246 $NNN_PAGER < "$FIFOPATH" & 247 printf "%s" "$!" > "$PREVIEWPID" 248 249 ( 250 exec > "$FIFOPATH" 251 if [ "$cmd" = "pager" ]; then 252 if exists bat; then 253 bat --terminal-width="$cols" --decorations=always --color=always \ 254 --paging=never --style="$NNN_BATSTYLE" --theme="$NNN_BATTHEME" "$@" & 255 else 256 $NNN_PAGER "$@" & 257 fi 258 else 259 "$cmd" "$@" & 260 fi 261 ) 262 263 rm -- "$FIFOPATH" 264 } 265 266 # Binary file: show file info inside the pager 267 print_bin_info() { 268 printf -- "-------- \033[1;31mBinary file\033[0m --------\n" 269 if exists mediainfo; then 270 mediainfo "$1" 271 else 272 file -b "$1" 273 fi 274 } 275 276 handle_mime() { 277 case "$2" in 278 image/jpeg) image_preview "$cols" "$lines" "$1" ;; 279 image/gif) generate_preview "$cols" "$lines" "$1" "gif" ;; 280 image/vnd.djvu) generate_preview "$cols" "$lines" "$1" "djvu" ;; 281 image/*) generate_preview "$cols" "$lines" "$1" "image" ;; 282 video/*) generate_preview "$cols" "$lines" "$1" "video" ;; 283 audio/*) generate_preview "$cols" "$lines" "$1" "audio" ;; 284 application/font*|application/*opentype|font/*) generate_preview "$cols" "$lines" "$1" "font" ;; 285 */*office*|*/*document*|*/*msword|*/*ms-excel) generate_preview "$cols" "$lines" "$1" "office" ;; 286 application/zip) fifo_pager unzip -l "$1" ;; 287 text/troff) 288 if exists man; then 289 fifo_pager man -Pcat -l "$1" 290 else 291 fifo_pager pager "$1" 292 fi ;; 293 *) handle_ext "$1" "$3" "$4" ;; 294 esac 295 } 296 297 handle_ext() { 298 case "$2" in 299 epub) generate_preview "$cols" "$lines" "$1" "epub" ;; 300 pdf) generate_preview "$cols" "$lines" "$1" "pdf" ;; 301 gz|bz2) fifo_pager tar -tvf "$1" ;; 302 md) if exists glow; then 303 fifo_pager glow -s dark "$1" 304 elif exists lowdown; then 305 fifo_pager lowdown -Tterm "$1" 306 else 307 fifo_pager pager "$1" 308 fi ;; 309 htm|html|xhtml) 310 if exists w3m; then 311 fifo_pager w3m "$1" 312 elif exists lynx; then 313 fifo_pager lynx "$1" 314 elif exists elinks; then 315 fifo_pager elinks "$1" 316 else 317 fifo_pager pager "$1" 318 fi ;; 319 7z|a|ace|alz|arc|arj|bz|cab|cpio|deb|jar|lha|lz|lzh|lzma|lzo\ 320 |rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z) 321 if exists atool; then 322 fifo_pager atool -l "$1" 323 elif exists bsdtar; then 324 fifo_pager bsdtar -tvf "$1" 325 fi ;; 326 *) if [ "$3" = "bin" ]; then 327 fifo_pager print_bin_info "$1" 328 else 329 fifo_pager pager "$1" 330 fi ;; 331 esac 332 } 333 334 preview_file() { 335 clear 336 # Trying to use pistol if it's available. 337 if [ "$NNN_PISTOL" -ne 0 ] && exists pistol; then 338 fifo_pager pistol "$1" 339 return 340 fi 341 342 # Trying to use scope.sh if it's available. 343 if [ "$NNN_SCOPE" -ne 0 ] && exists scope.sh; then 344 fifo_pager scope.sh "$1" "$cols" "$lines" "$(mktemp -d)" "True" 345 return 346 fi 347 348 # Use QuickLook if it's available. 349 if [ -n "$QUICKLOOK" ]; then 350 stat "$1" && f="$(wslpath -w "$1")" && "$QLPATH" "$f" & 351 return 352 fi 353 354 # Detecting the exact type of the file: the encoding, mime type, and extension in lowercase. 355 encoding="$(file -bL --mime-encoding -- "$1")" 356 mimetype="$(file -bL --mime-type -- "$1")" 357 ext="${1##*.}" 358 [ -n "$ext" ] && ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" 359 lines=$(tput lines) 360 cols=$(tput cols) 361 362 # Otherwise, falling back to the defaults. 363 if [ -d "$1" ]; then 364 cd "$1" || return 365 if [ "$NNN_ICONLOOKUP" -ne 0 ] && [ -f "$(dirname "$0")"/.iconlookup ]; then 366 [ "$NNN_SPLIT" = v ] && BSTR="\n" 367 # shellcheck disable=SC2012 368 ls -F --group-directories-first | head -n "$((lines - 3))" | "$(dirname "$0")"/.iconlookup -l "$cols" -B "$BSTR" -b " " 369 elif exists lsd; then 370 lsd -l --blocks=permission,name 371 elif exists tree; then 372 fifo_pager tree --filelimit "$(find . -maxdepth 1 | wc -l)" -L 3 -C -F --dirsfirst --noreport 373 elif exists exa; then 374 fifo_pager exa -T --group-directories-first --colour=always -L 3 375 elif exists eza; then # eza is a community fork of exa (exa is unmaintained) 376 fifo_pager eza -T --group-directories-first --colour=always -L 3 377 else 378 fifo_pager ls -F --group-directories-first --color=always 379 fi 380 cd .. 381 elif [ "${encoding#*)}" = "binary" ]; then 382 handle_mime "$1" "$mimetype" "$ext" "bin" 383 else 384 handle_mime "$1" "$mimetype" "$ext" 385 fi 386 } 387 388 generate_preview() { 389 if [ -n "$QLPATH" ] && stat "$3"; then 390 f="$(wslpath -w "$3")" && "$QLPATH" "$f" & 391 elif [ -n "$NNN_PREVIEWVIDEO" ] && [[ "$4" == +(gif|video) ]]; then 392 [ "$4" = "video" ] && args=(--start=10% --length=4) || args=() 393 video_preview "$1" "$2" "$3" "${args[@]}" && return 394 elif [ ! -f "$NNN_PREVIEWDIR/$3.jpg" ] || [ -n "$(find -L "$3" -newer "$NNN_PREVIEWDIR/$3.jpg")" ]; then 395 mkdir -p "$NNN_PREVIEWDIR/${3%/*}" 396 case $4 in 397 audio) ffmpeg -i "$3" -filter_complex "scale=iw*min(1\,min($NNN_PREVIEWWIDTH/iw\,ih)):-1" "$NNN_PREVIEWDIR/$3.jpg" -y ;; 398 epub) gnome-epub-thumbnailer "$3" "$NNN_PREVIEWDIR/$3.jpg" ;; 399 font) fontpreview -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" ;; 400 gif) if [ -p "$FIFO_UEBERZUG" ] && exists convert; then 401 frameprefix="$NNN_PREVIEWDIR/$3/${3##*/}" 402 if [ ! -d "$NNN_PREVIEWDIR/$3" ]; then 403 mkdir -p "$NNN_PREVIEWDIR/$3" 404 convert -coalesce -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$3" "$frameprefix.jpg" || 405 MAGICK_TMPDIR="/tmp" convert -coalesce -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$3" "$frameprefix.jpg" 406 fi 407 frames=$(($(find "$NNN_PREVIEWDIR/$3" | wc -l) - 2)) 408 [ $frames -lt 0 ] && return 409 while true; do 410 for i in $(seq 0 $frames); do 411 image_preview "$1" "$2" "$frameprefix-$i.jpg" 412 sleep 0.1 413 done 414 done & 415 printf "%s" "$!" > "$PREVIEWPID" 416 return 417 elif [ -n "$NNN_PREVIEWVIDEO" ]; then 418 video_preview "$1" "$2" "$3" && return 419 else 420 image_preview "$1" "$2" "$3" && return 421 fi ;; 422 image) if exists rsvg-convert && [[ "${3##*.}" == "svg" ]]; then 423 rsvg-convert -a -w "$NNN_PREVIEWWIDTH" -h "$NNN_PREVIEWHEIGHT" -f png -o "$NNN_PREVIEWDIR/$3.png" "$3" 424 elif exists convert; then 425 convert "$3" -flatten -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$NNN_PREVIEWDIR/$3.jpg" 426 else 427 image_preview "$1" "$2" "$3" && return 428 fi ;; 429 office) libreoffice --convert-to jpg "$3" --outdir "$NNN_PREVIEWDIR/${3%/*}" 430 filename="$(printf "%s" "${3##*/}" | cut -d. -f1)" 431 mv -- "$NNN_PREVIEWDIR/${3%/*}/$filename.jpg" "$NNN_PREVIEWDIR/$3.jpg" ;; 432 pdf) pdftoppm -jpeg -f 1 -singlefile "$3" "$NNN_PREVIEWDIR/$3" ;; 433 djvu) ddjvu -format=ppm -page=1 "$3" "$NNN_PREVIEWDIR/$3.jpg" ;; 434 video) video_preview "$1" "$2" "$3" && return ;; 435 esac 436 fi 437 if [ -f "$NNN_PREVIEWDIR/$3.jpg" ]; then 438 image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.jpg" 439 elif [[ "${3##*.}" == "svg" ]] && [ -f "$NNN_PREVIEWDIR/$3.png" ]; then 440 image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.png" 441 else 442 fifo_pager print_bin_info "$3" 443 fi 444 } >/dev/null 2>&1 445 446 image_preview() { 447 clear 448 exec >/dev/tty 449 if [ "$NNN_TERMINAL" = "kitty" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|icat) ]]; then 450 kitty +kitten icat --silent --scale-up --place "$1"x"$2"@0x0 --transfer-mode=stream --stdin=no "$3" & 451 elif [ "$NNN_TERMINAL" = "tmux" ] && [[ -n "$KITTY_LISTEN_ON" ]] && [[ "$NNN_PREVIEWIMGPROG" == +(|icat) ]]; then 452 kitty +kitten icat --silent --scale-up --place "$(($1 - 1))x$(($2 - 1))"@0x0 --transfer-mode=memory --stdin=no "$3" & 453 elif [ "$NNN_TERMINAL" = "wezterm" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|imgcat) ]]; then 454 wezterm imgcat "$3" & 455 elif exists ueberzug && [[ "$NNN_PREVIEWIMGPROG" == +(|ueberzug) ]]; then 456 ueberzug_layer "$1" "$2" "$3" && return 457 elif exists catimg && [[ "$NNN_PREVIEWIMGPROG" == +(|catimg) ]]; then 458 catimg "$3" & 459 elif exists viu && [[ "$NNN_PREVIEWIMGPROG" == +(|viu) ]]; then 460 viu -t "$3" & 461 elif exists chafa && [[ "$NNN_PREVIEWIMGPROG" == +(|chafa) ]]; then 462 chafa "$3" & 463 elif exists img2sixel && [[ "$NNN_PREVIEWIMGPROG" == +(|img2sixel) ]]; then 464 img2sixel -g "$3" & 465 else 466 fifo_pager print_bin_info "$3" && return 467 fi 468 printf "%s" "$!" > "$PREVIEWPID" 469 } 470 471 video_preview() { 472 clear 473 exec >/dev/tty 474 if [ -n "$NNN_PREVIEWVIDEO" ]; then 475 mpv --no-config --really-quiet --vo="$NNN_PREVIEWVIDEO" --profile=sw-fast --loop-file --no-audio "$4" "$3" & 476 else 477 ffmpegthumbnailer -m -s0 -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" || rm -- "$NNN_PREVIEWDIR/$3.jpg" & 478 fi 479 printf "%s" "$!" > "$PREVIEWPID" 480 } 481 482 ueberzug_layer() { 483 [ -f "$POSOFFSET" ] && read -r x y < "$POSOFFSET" 484 printf '{"action": "add", "identifier": "nnn_ueberzug", "x": %d, "y": %d, "width": "%d", "height": "%d", "scaler": "fit_contain", "path": "%s"}\n'\ 485 "${x:-0}" "${y:-0}" "$1" "$2" "$3" > "$FIFO_UEBERZUG" 486 } 487 488 ueberzug_remove() { 489 printf '{"action": "remove", "identifier": "nnn_ueberzug"}\n' > "$FIFO_UEBERZUG" 490 } 491 492 winch_handler() { 493 clear 494 pidkill "$PREVIEWPID" 495 if [ -p "$FIFO_UEBERZUG" ]; then 496 pkill -f "tail --follow $FIFO_UEBERZUG" 497 tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & 498 fi 499 preview_file "$(cat "$CURSEL")" 500 } 501 502 preview_fifo() { 503 while read -r selection; do 504 if [ -n "$selection" ]; then 505 pidkill "$PREVIEWPID" 506 [ -p "$FIFO_UEBERZUG" ] && ueberzug_remove 507 [ "$selection" = "close" ] && break 508 preview_file "$selection" 509 printf "%s" "$selection" > "$CURSEL" 510 fi 511 done < "$NNN_FIFO" 512 sleep 0.1 # make sure potential preview by winch_handler is killed 513 pkill -P "$$" 514 } 515 516 if [ "$PREVIEW_MODE" -eq 1 ] 2>/dev/null; then 517 if exists ueberzug && [ "$NNN_TERMINAL" != "kitty" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|ueberzug) ]]; then 518 mkfifo "$FIFO_UEBERZUG" 519 tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & 520 fi 521 522 preview_file "$PWD/$1" 523 preview_fifo & WAITPID=$! 524 printf "%s" "$!" > "$FIFOPID" 525 printf "%s" "$PWD/$1" > "$CURSEL" 526 trap 'winch_handler' WINCH 527 trap 'rm -- "$PREVIEWPID" "$CURSEL" "$FIFO_UEBERZUG" "$FIFOPID" "$POSOFFSET" 2>/dev/null' INT HUP EXIT 528 while kill -s 0 $WAITPID; do 529 wait $WAITPID 2>/dev/null 530 done 531 exit 0 532 else 533 if [ ! -r "$NNN_FIFO" ]; then 534 prompt "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')\nPlease read Usage in '$0'." 535 elif [ "$KITTY_WINDOW_ID" ] && [ -z "$TMUX" ] && [ -z "$KITTY_LISTEN_ON" ]; then 536 prompt "\$KITTY_LISTEN_ON not set!\nPlease read Usage in '$0'." 537 else 538 toggle_preview "$1" & 539 fi 540 fi