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