My build of nnn with minor changes
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

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