My build of nnn with minor changes
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

491 строка
16 KiB

  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. mocplay 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. # i. by extension
  25. # ii. by mime (image, video, audio, pdf)
  26. # iii. by mime (other file types)
  27. #
  28. # Modification tips:
  29. # 1. Invokes CLI utilities by default. Set GUI to 1 to enable GUI apps.
  30. # 2. PAGER is "less -R".
  31. # 3. Start GUI apps in bg to unblock. Redirect stdout and strerr if required.
  32. # 4. Some CLI utilities are piped to the $PAGER, to wait and quit uniformly.
  33. # 5. If the output cannot be paged use "read -r _" to wait for user input.
  34. # 6. On a DE, try 'xdg-open' in handle_fallback() as last resort.
  35. #
  36. # Feel free to change the utilities to your favourites and add more mimes.
  37. #
  38. # Defaults:
  39. # By extension (only the enabled ones):
  40. # most archives: list with atool, bsdtar
  41. # rar: list with unrar
  42. # 7-zip: list with 7z
  43. # pdf: zathura (GUI), pdftotext, mutool, exiftool
  44. # audio: mocplay (nnn plugin using MOC), mpv, mediainfo, exiftool
  45. # avi|mkv|mp4: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool
  46. # log: vi
  47. # torrent: rtorrent, transmission-show
  48. # odt|ods|odp|sxw: odt2txt
  49. # md: glow (https://github.com/charmbracelet/glow), lowdown (https://kristaps.bsd.lv/lowdown)
  50. # htm|html|xhtml: w3m, lynx, elinks
  51. # json: jq, python (json.tool module)
  52. # Multimedia by mime:
  53. # image/*: sxiv (GUI), viu (https://github.com/atanunq/viu), img2txt, exiftool
  54. # video/*: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool
  55. # audio/*: mocplay (nnn plugin using MOC), mpv, mediainfo, exiftool
  56. # application/pdf: zathura (GUI), pdftotext, mutool, exiftool
  57. # Other mimes:
  58. # text/troff: man -l
  59. # text/* | */xml: vi
  60. # image/vnd.djvu): djvutxt, exiftool
  61. #
  62. # ToDo:
  63. # 1. Adapt, test and enable all mimes
  64. # 2. Clean-up the unnecessary exit codes
  65. # #############################################################################
  66. # set to 1 to enable GUI apps
  67. GUI="${GUI:-0}"
  68. set -euf -o noclobber -o noglob -o nounset
  69. IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n
  70. PATH=$PATH:"${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins"
  71. IMAGE_CACHE_PATH="$(dirname "$1")"/.thumbs
  72. FPATH="$1"
  73. FNAME=$(basename "$1")
  74. EDITOR="${EDITOR:-vi}"
  75. PAGER="${PAGER:-less -R}"
  76. ext="${FNAME##*.}"
  77. if ! [ -z "$ext" ]; then
  78. ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')"
  79. fi
  80. handle_pdf() {
  81. if [ "$GUI" -ne 0 ] && which zathura >/dev/null 2>&1; then
  82. zathura "${FPATH}" >/dev/null 2>&1 &
  83. exit 0
  84. elif which pdftotext >/dev/null 2>&1; then
  85. ## Preview as text conversion
  86. pdftotext -l 10 -nopgbrk -q -- "${FPATH}" - | eval "$PAGER"
  87. exit 0
  88. elif which mutool >/dev/null 2>&1; then
  89. mutool draw -F txt -i -- "${FPATH}" 1-10
  90. exit 0
  91. elif which exiftool >/dev/null 2>&1; then
  92. exiftool "${FPATH}" | eval "$PAGER"
  93. exit 0
  94. fi
  95. }
  96. handle_audio() {
  97. if which mocp >/dev/null 2>&1 && which mocplay >/dev/null 2>&1; then
  98. mocplay "${FPATH}" "opener" >/dev/null 2>&1
  99. exit 0
  100. elif which mpv >/dev/null 2>&1; then
  101. mpv "${FPATH}" >/dev/null 2>&1 &
  102. exit 0
  103. elif which mediainfo >/dev/null 2>&1; then
  104. mediainfo "${FPATH}" | eval "$PAGER"
  105. exit 0
  106. elif which exiftool >/dev/null 2>&1; then
  107. exiftool "${FPATH}"| eval "$PAGER"
  108. exit 0
  109. fi
  110. }
  111. handle_video() {
  112. if [ "$GUI" -ne 0 ] && which smplayer >/dev/null 2>&1; then
  113. smplayer "${FPATH}" >/dev/null 2>&1 &
  114. exit 0
  115. elif [ "$GUI" -ne 0 ] && which mpv >/dev/null 2>&1; then
  116. mpv "${FPATH}" >/dev/null 2>&1 &
  117. exit 0
  118. elif which ffmpegthumbnailer >/dev/null 2>&1; then
  119. # Thumbnail
  120. [ -d "${IMAGE_CACHE_PATH}" ] || mkdir "${IMAGE_CACHE_PATH}"
  121. ffmpegthumbnailer -i "${FPATH}" -o "${IMAGE_CACHE_PATH}/${FNAME}.jpg" -s 0
  122. viu -n "${IMAGE_CACHE_PATH}/${FNAME}.jpg" | eval "$PAGER"
  123. exit 0
  124. elif which mediainfo >/dev/null 2>&1; then
  125. mediainfo "${FPATH}" | eval "$PAGER"
  126. exit 0
  127. elif which exiftool >/dev/null 2>&1; then
  128. exiftool "${FPATH}"| eval "$PAGER"
  129. exit 0
  130. fi
  131. }
  132. # handle this extension and exit
  133. handle_extension() {
  134. case "${ext}" in
  135. ## Archive
  136. a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\
  137. rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip)
  138. if which atool >/dev/null 2>&1; then
  139. atool --list -- "${FPATH}" | eval "$PAGER"
  140. exit 0
  141. elif which bsdtar >/dev/null 2>&1; then
  142. bsdtar --list --file "${FPATH}" | eval "$PAGER"
  143. exit 0
  144. fi
  145. exit 1;;
  146. rar)
  147. if which unrar >/dev/null 2>&1; then
  148. ## Avoid password prompt by providing empty password
  149. unrar lt -p- -- "${FPATH}" | eval "$PAGER"
  150. fi
  151. exit 1;;
  152. 7z)
  153. if which 7z >/dev/null 2>&1; then
  154. ## Avoid password prompt by providing empty password
  155. 7z l -p -- "${FPATH}" | eval "$PAGER"
  156. exit 0
  157. fi
  158. exit 1;;
  159. ## PDF
  160. pdf)
  161. handle_pdf
  162. exit 1;;
  163. ## Audio
  164. aac|flac|m4a|mid|midi|mpa|mp2|mp3|ogg|wav|wma)
  165. handle_audio
  166. exit 1;;
  167. ## Video
  168. avi|mkv|mp4)
  169. handle_video
  170. exit 1;;
  171. ## Log files
  172. log)
  173. "$EDITOR" "${FPATH}"
  174. exit 0;;
  175. ## BitTorrent
  176. torrent)
  177. if which rtorrent >/dev/null 2>&1; then
  178. rtorrent "${FPATH}"
  179. exit 0
  180. elif which transmission-show >/dev/null 2>&1; then
  181. transmission-show -- "${FPATH}"
  182. exit 0
  183. fi
  184. exit 1;;
  185. ## OpenDocument
  186. odt|ods|odp|sxw)
  187. if which odt2txt >/dev/null 2>&1; then
  188. ## Preview as text conversion
  189. odt2txt "${FPATH}" | eval "$PAGER"
  190. exit 0
  191. fi
  192. exit 1;;
  193. ## Markdown
  194. md)
  195. if which glow >/dev/null 2>&1; then
  196. glow -sdark "${FPATH}" | eval "$PAGER"
  197. exit 0
  198. elif which lowdown >/dev/null 2>&1; then
  199. lowdown -Tterm "${FPATH}" | eval "$PAGER"
  200. exit 0
  201. fi
  202. ;;
  203. ## HTML
  204. htm|html|xhtml)
  205. ## Preview as text conversion
  206. if which w3m >/dev/null 2>&1; then
  207. w3m -dump "${FPATH}" | eval "$PAGER"
  208. exit 0
  209. elif which lynx >/dev/null 2>&1; then
  210. lynx -dump -- "${FPATH}" | eval "$PAGER"
  211. exit 0
  212. elif which elinks >/dev/null 2>&1; then
  213. elinks -dump "${FPATH}" | eval "$PAGER"
  214. exit 0
  215. fi
  216. ;;
  217. ## JSON
  218. json)
  219. if which jq >/dev/null 2>&1; then
  220. jq --color-output . "${FPATH}" | eval "$PAGER"
  221. exit 0
  222. elif which python >/dev/null 2>&1; then
  223. python -m json.tool -- "${FPATH}" | eval "$PAGER"
  224. exit 0
  225. fi
  226. ;;
  227. esac
  228. }
  229. abspath() {
  230. case "$1" in
  231. /*) printf "%s\n" "$1";;
  232. *) printf "%s\n" "$PWD/$1";;
  233. esac
  234. }
  235. listimages() {
  236. find -L "$(dirname "$target")" -maxdepth 1 -type f -iregex \
  237. '.*\(jpe?g\|bmp\|png\|gif\)$' -print0 | sort -z
  238. }
  239. sxiv_load_dir() {
  240. target="$(abspath "$1")"
  241. count="$(listimages | grep -a -m 1 -ZznF "$target" | cut -d: -f1)"
  242. if [ -n "$count" ]; then
  243. listimages | xargs -0 sxiv -n "$count" --
  244. else
  245. sxiv -- "$@" # fallback
  246. fi
  247. }
  248. handle_multimedia() {
  249. ## Size of the preview if there are multiple options or it has to be
  250. ## rendered from vector graphics. If the conversion program allows
  251. ## specifying only one dimension while keeping the aspect ratio, the width
  252. ## will be used.
  253. # local DEFAULT_SIZE="1920x1080"
  254. mimetype="${1}"
  255. case "${mimetype}" in
  256. ## SVG
  257. # image/svg+xml|image/svg)
  258. # convert -- "${FPATH}" "${IMAGE_CACHE_PATH}" && exit 6
  259. # exit 1;;
  260. ## DjVu
  261. # image/vnd.djvu)
  262. # ddjvu -format=tiff -quality=90 -page=1 -size="${DEFAULT_SIZE}" \
  263. # - "${IMAGE_CACHE_PATH}" < "${FPATH}" \
  264. # && exit 6 || exit 1;;
  265. ## Image
  266. image/*)
  267. if [ "$GUI" -ne 0 ] && which sxiv >/dev/null 2>&1; then
  268. sxiv_load_dir "${FPATH}" >/dev/null 2>&1 &
  269. exit 0
  270. elif which viu >/dev/null 2>&1; then
  271. viu -n "${FPATH}" | eval "$PAGER"
  272. exit 0
  273. elif which img2txt >/dev/null 2>&1; then
  274. img2txt --gamma=0.6 -- "${FPATH}" | eval "$PAGER"
  275. exit 0
  276. elif which exiftool >/dev/null 2>&1; then
  277. exiftool "${FPATH}" | eval "$PAGER"
  278. exit 0
  279. fi
  280. # local orientation
  281. # orientation="$( identify -format '%[EXIF:Orientation]\n' -- "${FPATH}" )"
  282. ## If orientation data is present and the image actually
  283. ## needs rotating ("1" means no rotation)...
  284. # if [[ -n "$orientation" && "$orientation" != 1 ]]; then
  285. ## ...auto-rotate the image according to the EXIF data.
  286. # convert -- "${FPATH}" -auto-orient "${IMAGE_CACHE_PATH}" && exit 6
  287. # fi
  288. ## `w3mimgdisplay` will be called for all images (unless overriden
  289. ## as above), but might fail for unsupported types.
  290. exit 7;;
  291. ## PDF
  292. application/pdf)
  293. handle_pdf
  294. exit 1;;
  295. ## Audio
  296. audio/*)
  297. handle_audio
  298. exit 1;;
  299. ## Video
  300. video/*)
  301. handle_video
  302. exit 1;;
  303. # pdftoppm -f 1 -l 1 \
  304. # -scale-to-x "${DEFAULT_SIZE%x*}" \
  305. # -scale-to-y -1 \
  306. # -singlefile \
  307. # -jpeg -tiffcompression jpeg \
  308. # -- "${FPATH}" "${IMAGE_CACHE_PATH%.*}" \
  309. # && exit 6 || exit 1;;
  310. ## ePub, MOBI, FB2 (using Calibre)
  311. # application/epub+zip|application/x-mobipocket-ebook|\
  312. # application/x-fictionbook+xml)
  313. # # ePub (using https://github.com/marianosimone/epub-thumbnailer)
  314. # epub-thumbnailer "${FPATH}" "${IMAGE_CACHE_PATH}" \
  315. # "${DEFAULT_SIZE%x*}" && exit 6
  316. # ebook-meta --get-cover="${IMAGE_CACHE_PATH}" -- "${FPATH}" \
  317. # >/dev/null && exit 6
  318. # exit 1;;
  319. ## Font
  320. # application/font*|application/*opentype)
  321. # preview_png="/tmp/$(basename "${IMAGE_CACHE_PATH%.*}").png"
  322. # if fontimage -o "${preview_png}" \
  323. # --pixelsize "120" \
  324. # --fontname \
  325. # --pixelsize "80" \
  326. # --text " ABCDEFGHIJKLMNOPQRSTUVWXYZ " \
  327. # --text " abcdefghijklmnopqrstuvwxyz " \
  328. # --text " 0123456789.:,;(*!?') ff fl fi ffi ffl " \
  329. # --text " The quick brown fox jumps over the lazy dog. " \
  330. # "${FPATH}";
  331. # then
  332. # convert -- "${preview_png}" "${IMAGE_CACHE_PATH}" \
  333. # && rm "${preview_png}" \
  334. # && exit 6
  335. # else
  336. # exit 1
  337. # fi
  338. # ;;
  339. ## Preview archives using the first image inside.
  340. ## (Very useful for comic book collections for example.)
  341. # application/zip|application/x-rar|application/x-7z-compressed|\
  342. # application/x-xz|application/x-bzip2|application/x-gzip|application/x-tar)
  343. # local fn=""; local fe=""
  344. # local zip=""; local rar=""; local tar=""; local bsd=""
  345. # case "${mimetype}" in
  346. # application/zip) zip=1 ;;
  347. # application/x-rar) rar=1 ;;
  348. # application/x-7z-compressed) ;;
  349. # *) tar=1 ;;
  350. # esac
  351. # { [ "$tar" ] && fn=$(tar --list --file "${FPATH}"); } || \
  352. # { fn=$(bsdtar --list --file "${FPATH}") && bsd=1 && tar=""; } || \
  353. # { [ "$rar" ] && fn=$(unrar lb -p- -- "${FPATH}"); } || \
  354. # { [ "$zip" ] && fn=$(zipinfo -1 -- "${FPATH}"); } || return
  355. #
  356. # fn=$(echo "$fn" | python -c "import sys; import mimetypes as m; \
  357. # [ print(l, end='') for l in sys.stdin if \
  358. # (m.guess_type(l[:-1])[0] or '').startswith('image/') ]" |\
  359. # sort -V | head -n 1)
  360. # [ "$fn" = "" ] && return
  361. # [ "$bsd" ] && fn=$(printf '%b' "$fn")
  362. #
  363. # [ "$tar" ] && tar --extract --to-stdout \
  364. # --file "${FPATH}" -- "$fn" > "${IMAGE_CACHE_PATH}" && exit 6
  365. # fe=$(echo -n "$fn" | sed 's/[][*?\]/\\\0/g')
  366. # [ "$bsd" ] && bsdtar --extract --to-stdout \
  367. # --file "${FPATH}" -- "$fe" > "${IMAGE_CACHE_PATH}" && exit 6
  368. # [ "$bsd" ] || [ "$tar" ] && rm -- "${IMAGE_CACHE_PATH}"
  369. # [ "$rar" ] && unrar p -p- -inul -- "${FPATH}" "$fn" > \
  370. # "${IMAGE_CACHE_PATH}" && exit 6
  371. # [ "$zip" ] && unzip -pP "" -- "${FPATH}" "$fe" > \
  372. # "${IMAGE_CACHE_PATH}" && exit 6
  373. # [ "$rar" ] || [ "$zip" ] && rm -- "${IMAGE_CACHE_PATH}"
  374. # ;;
  375. esac
  376. }
  377. handle_mime() {
  378. mimetype="${1}"
  379. case "${mimetype}" in
  380. ## Manpages
  381. text/troff)
  382. man -l "${FPATH}"
  383. exit 0;;
  384. ## Text
  385. text/* | */xml)
  386. "$EDITOR" "${FPATH}"
  387. exit 0;;
  388. ## Syntax highlight
  389. # if [[ "$( stat --printf='%s' -- "${FPATH}" )" -gt "${HIGHLIGHT_SIZE_MAX}" ]]; then
  390. # exit 2
  391. # fi
  392. # if [[ "$( tput colors )" -ge 256 ]]; then
  393. # local pygmentize_format='terminal256'
  394. # local highlight_format='xterm256'
  395. # else
  396. # local pygmentize_format='terminal'
  397. # local highlight_format='ansi'
  398. # fi
  399. # env HIGHLIGHT_OPTIONS="${HIGHLIGHT_OPTIONS}" highlight \
  400. # --out-format="${highlight_format}" \
  401. # --force -- "${FPATH}" && exit 5
  402. # pygmentize -f "${pygmentize_format}" -O "style=${PYGMENTIZE_STYLE}"\
  403. # -- "${FPATH}" && exit 5
  404. # exit 2;;
  405. ## DjVu
  406. image/vnd.djvu)
  407. if which djvutxt >/dev/null 2>&1; then
  408. ## Preview as text conversion (requires djvulibre)
  409. djvutxt "${FPATH}" | eval "$PAGER"
  410. exit 0
  411. elif which exiftool >/dev/null 2>&1; then
  412. exiftool "${FPATH}" | eval "$PAGER"
  413. exit 0
  414. fi
  415. exit 1;;
  416. esac
  417. }
  418. handle_fallback() {
  419. if [ "$GUI" -ne 0 ]; then
  420. xdg-open "${FPATH}" >/dev/null 2>&1 &
  421. exit 0
  422. fi
  423. echo '----- File details -----' && file --dereference --brief -- "${FPATH}"
  424. exit 1
  425. }
  426. handle_blocked() {
  427. case "${MIMETYPE}" in
  428. application/x-sharedlib)
  429. exit 0;;
  430. application/x-shared-library-la)
  431. exit 0;;
  432. application/x-executable)
  433. exit 0;;
  434. application/x-shellscript)
  435. exit 0;;
  436. esac
  437. }
  438. MIMETYPE="$( file --dereference --brief --mime-type -- "${FPATH}" )"
  439. handle_blocked "${MIMETYPE}"
  440. handle_extension
  441. handle_multimedia "${MIMETYPE}"
  442. handle_mime "${MIMETYPE}"
  443. handle_fallback
  444. exit 1