nuke (18755B)
1 #!/usr/bin/env sh 2 3 # Description: Sample script to play files in apps by file type or mime 4 # 5 # Shell: POSIX compliant 6 # Usage: nuke filepath 7 # 8 # Integration with nnn: 9 # 1. Export the required config: 10 # export NNN_OPENER=/absolute/path/to/nuke 11 # # Otherwise, if nuke is in $PATH 12 # # export NNN_OPENER=nuke 13 # 2. Run nnn with the program option to indicate a CLI opener 14 # nnn -c 15 # # The -c program option overrides option -e 16 # 3. nuke can use nnn plugins (e.g. mocq is used for audio), $PATH is updated. 17 # 18 # Details: 19 # Inspired by ranger's scope.sh, modified for usage with nnn. 20 # 21 # Guards against accidentally opening mime types like executables, shared libs etc. 22 # 23 # Tries to play 'file' (1st argument) in the following order: 24 # 1. by extension 25 # 2. by mime (image, video, audio, pdf) 26 # 3. by mime (other file types) 27 # 4. by mime (prompt and run executables) 28 # 29 # Modification tips: 30 # 1. Invokes CLI utilities by default. Set GUI to 1 to enable GUI apps. 31 # 2. PAGER is "less -R". 32 # 3. Start GUI apps in bg to unblock. Redirect stdout and strerr if required. 33 # 4. Some CLI utilities are piped to the $PAGER, to wait and quit uniformly. 34 # 5. If the output cannot be paged use "read -r _" to wait for user input. 35 # 6. On a DE, try 'xdg-open' or 'open' in handle_fallback() as last resort. 36 # 37 # Feel free to change the utilities to your favourites and add more mimes. 38 # 39 # Defaults: 40 # By extension (only the enabled ones): 41 # most archives: list with atool, bsdtar 42 # rar: list with unrar 43 # 7-zip: list with 7z 44 # pdf: zathura (GUI), pdftotext, mutool, exiftool 45 # audio: mocq (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool 46 # avi|mkv|mp4: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool 47 # log: vi 48 # torrent: rtorrent, transmission-show 49 # odt|ods|odp|sxw: odt2txt 50 # md: glow (https://github.com/charmbracelet/glow), lowdown (https://kristaps.bsd.lv/lowdown) 51 # htm|html|xhtml: w3m, lynx, elinks 52 # json: jq, python (json.tool module) 53 # Multimedia by mime: 54 # image/*: imv/sxiv/nsxiv (GUI), viu (https://github.com/atanunq/viu), img2txt, exiftool 55 # video/*: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool 56 # audio/*: mocq (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool 57 # application/pdf: zathura (GUI), pdftotext, mutool, exiftool 58 # Other mimes: 59 # text/troff: man -l 60 # text/* | */xml: vi 61 # image/vnd.djvu): djvutxt, exiftool 62 # 63 # TODO: 64 # 1. Adapt, test and enable all mimes 65 # 2. Clean-up the unnecessary exit codes 66 67 # set to 1 to enable GUI apps and/or BIN execution 68 GUI="${GUI:-0}" 69 BIN="${BIN:-0}" 70 71 set -euf -o noclobber -o noglob -o nounset 72 IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n 73 74 PATH=$PATH:"${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins" 75 IMAGE_CACHE_PATH="$(dirname "$1")"/.thumbs 76 77 FPATH="$1" 78 FNAME=$(basename "$1") 79 EDITOR="${VISUAL:-${EDITOR:-vi}}" 80 PAGER="${PAGER:-less -R}" 81 ext="${FNAME##*.}" 82 if [ -n "$ext" ]; then 83 ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" 84 fi 85 86 is_mac() { 87 uname | grep -q "Darwin" 88 } 89 90 handle_pdf() { 91 if [ "$GUI" -ne 0 ]; then 92 if is_mac; then 93 nohup open "${FPATH}" >/dev/null 2>&1 & 94 elif type zathura >/dev/null 2>&1; then 95 nohup zathura "${FPATH}" >/dev/null 2>&1 & 96 else 97 return 98 fi 99 elif type pdftotext >/dev/null 2>&1; then 100 ## Preview as text conversion 101 pdftotext -l 10 -nopgbrk -q -- "${FPATH}" - | eval "$PAGER" 102 elif type mutool >/dev/null 2>&1; then 103 mutool draw -F txt -i -- "${FPATH}" 1-10 | eval "$PAGER" 104 elif type exiftool >/dev/null 2>&1; then 105 exiftool "${FPATH}" | eval "$PAGER" 106 else 107 return 108 fi 109 exit 0 110 } 111 112 handle_audio() { 113 if type mocp >/dev/null 2>&1 && type mocq >/dev/null 2>&1; then 114 mocq "${FPATH}" "opener" >/dev/null 2>&1 115 elif type mpv >/dev/null 2>&1; then 116 mpv "${FPATH}" >/dev/null 2>&1 & 117 elif type media_client >/dev/null 2>&1; then 118 media_client play "${FPATH}" >/dev/null 2>&1 & 119 elif type mediainfo >/dev/null 2>&1; then 120 mediainfo "${FPATH}" | eval "$PAGER" 121 elif type exiftool >/dev/null 2>&1; then 122 exiftool "${FPATH}"| eval "$PAGER" 123 else 124 return 125 fi 126 exit 0 127 } 128 129 handle_video() { 130 if [ "$GUI" -ne 0 ]; then 131 if is_mac; then 132 nohup open "${FPATH}" >/dev/null 2>&1 & 133 elif type smplayer >/dev/null 2>&1; then 134 nohup smplayer "${FPATH}" >/dev/null 2>&1 & 135 elif type mpv >/dev/null 2>&1; then 136 nohup mpv "${FPATH}" >/dev/null 2>&1 & 137 else 138 return 139 fi 140 elif type ffmpegthumbnailer >/dev/null 2>&1; then 141 # Thumbnail 142 [ -d "${IMAGE_CACHE_PATH}" ] || mkdir "${IMAGE_CACHE_PATH}" 143 ffmpegthumbnailer -i "${FPATH}" -o "${IMAGE_CACHE_PATH}/${FNAME}.jpg" -s 0 144 viu -n "${IMAGE_CACHE_PATH}/${FNAME}.jpg" | eval "$PAGER" 145 elif type mediainfo >/dev/null 2>&1; then 146 mediainfo "${FPATH}" | eval "$PAGER" 147 elif type exiftool >/dev/null 2>&1; then 148 exiftool "${FPATH}"| eval "$PAGER" 149 else 150 return 151 fi 152 exit 0 153 } 154 155 # handle this extension and exit 156 handle_extension() { 157 case "${ext}" in 158 ## Archive 159 a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ 160 rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip) 161 if type atool >/dev/null 2>&1; then 162 atool --list -- "${FPATH}" | eval "$PAGER" 163 exit 0 164 elif type bsdtar >/dev/null 2>&1; then 165 bsdtar --list --file "${FPATH}" | eval "$PAGER" 166 exit 0 167 fi 168 exit 1;; 169 rar) 170 if type unrar >/dev/null 2>&1; then 171 ## Avoid password prompt by providing empty password 172 unrar lt -p- -- "${FPATH}" | eval "$PAGER" 173 fi 174 exit 1;; 175 7z) 176 if type 7z >/dev/null 2>&1; then 177 ## Avoid password prompt by providing empty password 178 7z l -p -- "${FPATH}" | eval "$PAGER" 179 exit 0 180 fi 181 exit 1;; 182 183 ## PDF 184 pdf) 185 handle_pdf 186 exit 1;; 187 188 ## Audio 189 aac|flac|m4a|mid|midi|mpa|mp2|mp3|ogg|wav|wma) 190 handle_audio 191 exit 1;; 192 193 ## Video 194 avi|mkv|mp4) 195 handle_video 196 exit 1;; 197 198 ## Log files 199 log) 200 "$EDITOR" "${FPATH}" 201 exit 0;; 202 203 ## BitTorrent 204 torrent) 205 if type rtorrent >/dev/null 2>&1; then 206 rtorrent "${FPATH}" 207 exit 0 208 elif type transmission-show >/dev/null 2>&1; then 209 transmission-show -- "${FPATH}" 210 exit 0 211 fi 212 exit 1;; 213 214 ## OpenDocument 215 odt|ods|odp|sxw) 216 if type odt2txt >/dev/null 2>&1; then 217 ## Preview as text conversion 218 odt2txt "${FPATH}" | eval "$PAGER" 219 exit 0 220 fi 221 exit 1;; 222 223 ## Markdown 224 md) 225 if type glow >/dev/null 2>&1; then 226 glow -sdark "${FPATH}" | eval "$PAGER" 227 exit 0 228 elif type lowdown >/dev/null 2>&1; then 229 lowdown -Tterm "${FPATH}" | eval "$PAGER" 230 exit 0 231 fi 232 ;; 233 234 ## HTML 235 htm|html|xhtml) 236 ## Preview as text conversion 237 if type w3m >/dev/null 2>&1; then 238 w3m -dump "${FPATH}" | eval "$PAGER" 239 exit 0 240 elif type lynx >/dev/null 2>&1; then 241 lynx -dump -- "${FPATH}" | eval "$PAGER" 242 exit 0 243 elif type elinks >/dev/null 2>&1; then 244 elinks -dump "${FPATH}" | eval "$PAGER" 245 exit 0 246 fi 247 ;; 248 249 ## JSON 250 json) 251 if type jq >/dev/null 2>&1; then 252 jq --color-output . "${FPATH}" | eval "$PAGER" 253 exit 0 254 elif type python >/dev/null 2>&1; then 255 python -m json.tool -- "${FPATH}" | eval "$PAGER" 256 exit 0 257 fi 258 ;; 259 esac 260 } 261 262 # sets the variable abs_target, this should be faster than calling printf 263 abspath() { 264 case "$1" in 265 /*) abs_target="$1";; 266 *) abs_target="$PWD/$1";; 267 esac 268 } 269 270 # storing the result to a tmp file is faster than calling listimages twice 271 listimages() { 272 find -L "///${1%/*}" -maxdepth 1 -type f -print0 | 273 grep -izZE '\.(jpe?g|png|gif|webp|tiff|bmp|ico|svg)$' | 274 sort -z | tee "$tmp" 275 } 276 277 load_dir() { 278 abspath "$2" 279 tmp="${TMPDIR:-/tmp}/nuke_$$" 280 trap 'rm -f $tmp' EXIT 281 count="$(listimages "$abs_target" | grep -a -m 1 -ZznF "$abs_target" | cut -d: -f1)" 282 283 if [ -n "$count" ]; then 284 if [ "$GUI" -ne 0 ]; then 285 xargs -0 nohup "$1" -n "$count" -- < "$tmp" 286 else 287 xargs -0 "$1" -n "$count" -- < "$tmp" 288 fi 289 else 290 shift 291 "$1" -- "$@" # fallback 292 fi 293 } 294 295 handle_multimedia() { 296 ## Size of the preview if there are multiple options or it has to be 297 ## rendered from vector graphics. If the conversion program allows 298 ## specifying only one dimension while keeping the aspect ratio, the width 299 ## will be used. 300 # local DEFAULT_SIZE="1920x1080" 301 302 mimetype="${1}" 303 case "${mimetype}" in 304 ## SVG 305 # image/svg+xml|image/svg) 306 # convert -- "${FPATH}" "${IMAGE_CACHE_PATH}" && exit 6 307 # exit 1;; 308 309 ## DjVu 310 # image/vnd.djvu) 311 # ddjvu -format=tiff -quality=90 -page=1 -size="${DEFAULT_SIZE}" \ 312 # - "${IMAGE_CACHE_PATH}" < "${FPATH}" \ 313 # && exit 6 || exit 1;; 314 315 ## Image 316 image/*) 317 if [ "$GUI" -ne 0 ]; then 318 if is_mac; then 319 nohup open "${FPATH}" >/dev/null 2>&1 & 320 exit 0 321 elif type imv >/dev/null 2>&1; then 322 load_dir imv "${FPATH}" >/dev/null 2>&1 & 323 exit 0 324 elif type imvr >/dev/null 2>&1; then 325 load_dir imvr "${FPATH}" >/dev/null 2>&1 & 326 exit 0 327 elif type sxiv >/dev/null 2>&1; then 328 load_dir sxiv "${FPATH}" >/dev/null 2>&1 & 329 exit 0 330 elif type nsxiv >/dev/null 2>&1; then 331 load_dir nsxiv "${FPATH}" >/dev/null 2>&1 & 332 exit 0 333 fi 334 elif type viu >/dev/null 2>&1; then 335 viu -n "${FPATH}" | eval "$PAGER" 336 exit 0 337 elif type img2txt >/dev/null 2>&1; then 338 img2txt --gamma=0.6 -- "${FPATH}" | eval "$PAGER" 339 exit 0 340 elif type exiftool >/dev/null 2>&1; then 341 exiftool "${FPATH}" | eval "$PAGER" 342 exit 0 343 fi 344 # local orientation 345 # orientation="$( identify -format '%[EXIF:Orientation]\n' -- "${FPATH}" )" 346 ## If orientation data is present and the image actually 347 ## needs rotating ("1" means no rotation)... 348 # if [[ -n "$orientation" && "$orientation" != 1 ]]; then 349 ## ...auto-rotate the image according to the EXIF data. 350 # convert -- "${FPATH}" -auto-orient "${IMAGE_CACHE_PATH}" && exit 6 351 # fi 352 353 ## `w3mimgdisplay` will be called for all images (unless overridden 354 ## as above), but might fail for unsupported types. 355 exit 7;; 356 357 ## PDF 358 application/pdf) 359 handle_pdf 360 exit 1;; 361 362 ## Audio 363 audio/*) 364 handle_audio 365 exit 1;; 366 367 ## Video 368 video/*) 369 handle_video 370 exit 1;; 371 372 # pdftoppm -f 1 -l 1 \ 373 # -scale-to-x "${DEFAULT_SIZE%x*}" \ 374 # -scale-to-y -1 \ 375 # -singlefile \ 376 # -jpeg -tiffcompression jpeg \ 377 # -- "${FPATH}" "${IMAGE_CACHE_PATH%.*}" \ 378 # && exit 6 || exit 1;; 379 380 381 ## ePub, MOBI, FB2 (using Calibre) 382 # application/epub+zip|application/x-mobipocket-ebook|\ 383 # application/x-fictionbook+xml) 384 # # ePub (using https://github.com/marianosimone/epub-thumbnailer) 385 # epub-thumbnailer "${FPATH}" "${IMAGE_CACHE_PATH}" \ 386 # "${DEFAULT_SIZE%x*}" && exit 6 387 # ebook-meta --get-cover="${IMAGE_CACHE_PATH}" -- "${FPATH}" \ 388 # >/dev/null && exit 6 389 # exit 1;; 390 391 ## Font 392 # application/font*|application/*opentype) 393 # preview_png="/tmp/$(basename "${IMAGE_CACHE_PATH%.*}").png" 394 # if fontimage -o "${preview_png}" \ 395 # --pixelsize "120" \ 396 # --fontname \ 397 # --pixelsize "80" \ 398 # --text " ABCDEFGHIJKLMNOPQRSTUVWXYZ " \ 399 # --text " abcdefghijklmnopqrstuvwxyz " \ 400 # --text " 0123456789.:,;(*!?') ff fl fi ffi ffl " \ 401 # --text " The quick brown fox jumps over the lazy dog. " \ 402 # "${FPATH}"; 403 # then 404 # convert -- "${preview_png}" "${IMAGE_CACHE_PATH}" \ 405 # && rm "${preview_png}" \ 406 # && exit 6 407 # else 408 # exit 1 409 # fi 410 # ;; 411 412 ## Preview archives using the first image inside. 413 ## (Very useful for comic book collections for example.) 414 # application/zip|application/x-rar|application/x-7z-compressed|\ 415 # application/x-xz|application/x-bzip2|application/x-gzip|application/x-tar) 416 # local fn=""; local fe="" 417 # local zip=""; local rar=""; local tar=""; local bsd="" 418 # case "${mimetype}" in 419 # application/zip) zip=1 ;; 420 # application/x-rar) rar=1 ;; 421 # application/x-7z-compressed) ;; 422 # *) tar=1 ;; 423 # esac 424 # { [ "$tar" ] && fn=$(tar --list --file "${FPATH}"); } || \ 425 # { fn=$(bsdtar --list --file "${FPATH}") && bsd=1 && tar=""; } || \ 426 # { [ "$rar" ] && fn=$(unrar lb -p- -- "${FPATH}"); } || \ 427 # { [ "$zip" ] && fn=$(zipinfo -1 -- "${FPATH}"); } || return 428 # 429 # fn=$(echo "$fn" | python -c "import sys; import mimetypes as m; \ 430 # [ print(l, end='') for l in sys.stdin if \ 431 # (m.guess_type(l[:-1])[0] or '').startswith('image/') ]" |\ 432 # sort -V | head -n 1) 433 # [ "$fn" = "" ] && return 434 # [ "$bsd" ] && fn=$(printf '%b' "$fn") 435 # 436 # [ "$tar" ] && tar --extract --to-stdout \ 437 # --file "${FPATH}" -- "$fn" > "${IMAGE_CACHE_PATH}" && exit 6 438 # fe=$(echo -n "$fn" | sed 's/[][*?\]/\\\0/g') 439 # [ "$bsd" ] && bsdtar --extract --to-stdout \ 440 # --file "${FPATH}" -- "$fe" > "${IMAGE_CACHE_PATH}" && exit 6 441 # [ "$bsd" ] || [ "$tar" ] && rm -- "${IMAGE_CACHE_PATH}" 442 # [ "$rar" ] && unrar p -p- -inul -- "${FPATH}" "$fn" > \ 443 # "${IMAGE_CACHE_PATH}" && exit 6 444 # [ "$zip" ] && unzip -pP "" -- "${FPATH}" "$fe" > \ 445 # "${IMAGE_CACHE_PATH}" && exit 6 446 # [ "$rar" ] || [ "$zip" ] && rm -- "${IMAGE_CACHE_PATH}" 447 # ;; 448 esac 449 } 450 451 handle_mime() { 452 mimetype="${1}" 453 case "${mimetype}" in 454 ## Manpages 455 text/troff) 456 man -l "${FPATH}" 457 exit 0;; 458 459 ## Text 460 text/* | */xml) 461 "$EDITOR" "${FPATH}" 462 exit 0;; 463 ## Syntax highlight 464 # if [[ "$( stat --printf='%s' -- "${FPATH}" )" -gt "${HIGHLIGHT_SIZE_MAX}" ]]; then 465 # exit 2 466 # fi 467 # if [[ "$( tput colors )" -ge 256 ]]; then 468 # local pygmentize_format='terminal256' 469 # local highlight_format='xterm256' 470 # else 471 # local pygmentize_format='terminal' 472 # local highlight_format='ansi' 473 # fi 474 # env HIGHLIGHT_OPTIONS="${HIGHLIGHT_OPTIONS}" highlight \ 475 # --out-format="${highlight_format}" \ 476 # --force -- "${FPATH}" && exit 5 477 # pygmentize -f "${pygmentize_format}" -O "style=${PYGMENTIZE_STYLE}"\ 478 # -- "${FPATH}" && exit 5 479 # exit 2;; 480 481 ## DjVu 482 image/vnd.djvu) 483 if type djvutxt >/dev/null 2>&1; then 484 ## Preview as text conversion (requires djvulibre) 485 djvutxt "${FPATH}" | eval "$PAGER" 486 exit 0 487 elif type exiftool >/dev/null 2>&1; then 488 exiftool "${FPATH}" | eval "$PAGER" 489 exit 0 490 fi 491 exit 1;; 492 esac 493 } 494 495 handle_fallback() { 496 if [ "$GUI" -ne 0 ]; then 497 if type xdg-open >/dev/null 2>&1; then 498 nohup xdg-open "${FPATH}" >/dev/null 2>&1 & 499 exit 0 500 elif type open >/dev/null 2>&1; then 501 nohup open "${FPATH}" >/dev/null 2>&1 & 502 exit 0 503 fi 504 fi 505 506 echo '----- File details -----' && file --dereference --brief -- "${FPATH}" 507 exit 1 508 } 509 510 handle_blocked() { 511 case "${MIMETYPE}" in 512 application/x-sharedlib) 513 exit 0;; 514 515 application/x-shared-library-la) 516 exit 0;; 517 518 application/x-executable) 519 exit 0;; 520 521 application/x-shellscript) 522 exit 0;; 523 524 application/octet-stream) 525 exit 0;; 526 esac 527 } 528 529 handle_bin() { 530 case "${MIMETYPE}" in 531 application/x-executable|application/x-shellscript) 532 clear 533 echo '-------- Executable File --------' && file --dereference --brief -- "${FPATH}" 534 printf "Run executable (y/N/'a'rgs)? " 535 read -r answer 536 case "$answer" in 537 [Yy]* ) exec "${FPATH}";; 538 [Aa]* ) 539 printf "args: " 540 read -r args 541 exec "${FPATH}" "$args";; 542 [Nn]* ) exit;; 543 esac 544 esac 545 } 546 547 MIMETYPE="$( file -bL --mime-type -- "${FPATH}" )" 548 handle_extension 549 handle_multimedia "${MIMETYPE}" 550 handle_mime "${MIMETYPE}" 551 [ "$BIN" -ne 0 ] && [ -x "${FPATH}" ] && handle_bin 552 handle_blocked "${MIMETYPE}" 553 handle_fallback 554 555 exit 1