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