From 5826da920fdae35cd2abc705a559bf9e59cf94e6 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Sat, 7 Dec 2019 22:48:33 +0530 Subject: [PATCH] Add nplay: a custom opener --- README.md | 2 +- misc/nlay/README.md | 31 ---- misc/nlay/nlay | 105 ----------- misc/nlay/nlay.1 | 37 ---- misc/nplay/README.md | 26 +++ misc/nplay/nplay | 423 +++++++++++++++++++++++++++++++++++++++++++ nnn.1 | 4 +- 7 files changed, 453 insertions(+), 175 deletions(-) delete mode 100644 misc/nlay/README.md delete mode 100755 misc/nlay/nlay delete mode 100644 misc/nlay/nlay.1 create mode 100644 misc/nplay/README.md create mode 100755 misc/nplay/nplay diff --git a/README.md b/README.md index 99abf9bc..2135a77a 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ There is no config file. Associated files are stored under `${XDG_CONFIG_HOME:-$ | Example `export` | Description | | --- | --- | -| `NNN_OPENER=mimeopen` | custom file opener | +| `NNN_OPENER=nplay` | custom file opener ([nplay](https://github.com/jarun/nnn/blob/master/misc/nplay/nplay)) | | `NNN_BMS='d:~/Documents;D:~/Docs archive/'` | key-bookmark pairs [max 10] | | `NNN_PLUG='o:fzopen;m:nmount;x:_chmod +x $nnn'` | key-plugin (or cmd) pairs (:key to run) [max 15] | | `NNN_USE_EDITOR=1` | open text files in `$VISUAL` (else `$EDITOR`, fallback vi) | diff --git a/misc/nlay/README.md b/misc/nlay/README.md deleted file mode 100644 index 4671eeca..00000000 --- a/misc/nlay/README.md +++ /dev/null @@ -1,31 +0,0 @@ -## nlay - -`nlay` (*NnnpLAY*) is a customizable media type or action handler - -### Usage - -`nlay` is not used by `nnn` now. However, the bash script can be used to run desktop search utility or screensaver: - - nlay file type - file: absolute path to file ("" for an action) - type: type of media or action - -### Default apps - -* gnome-search-tool, catfish - file search -* vlock - terminal screensaver (alternatives - cmatrix, termsaver) - -### Perks - -- simple to modify (extensive in-file notes, comments and indicative code) -- handle files by category (e.g. plaintext, search, screensaver) -- support for multiple apps by order of preference -- run app in the foreground or silently detached in the background -- optionally add app arguments - -### Tips to modify - -- set `app=` in any category to change the player -- set `opts=` to use app options -- toggle `bg=` to enable or disable running app silently (`stdout` and `stderr` redirected to `/dev/null`) in background. E.g., vim (CLI) should be verbose and in the foreground while Sublime Text (GUI) can be started silently in the background. -- enable the commented out code under `ENABLE_FILE_TYPE_HANDLING` to handle specific file extensions e.g. use a different app than the one used for the category. diff --git a/misc/nlay/nlay b/misc/nlay/nlay deleted file mode 100755 index b3d587fb..00000000 --- a/misc/nlay/nlay +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env bash - -# ############################################################################# -# nlay: a customizable script to play files in different apps by file type -# -# usage: nlay file/path type/action -# -# MUST READ: -# -# 1. Feel free to change the default apps to your favourite ones. -# If you change the app for a group you may also need to modify the opts and -# bg settings. If bg is set the app is detached and started in the background -# in silent mode. -# -# The bg setting depends on personal preferences and type of utility, e.g., I -# would start vi (CLI) in the foreground but Sublime Text (GUI) in background. -# -# Check (and TOGGLE as you wish) the default bg settings. -# -# 2. Detached apps are not killed when nnn exits. Use kill(1) or killall(1) to -# stop console based background apps. -# -# 3. nlay is OVERWRITTEN during nnn upgrade. You can store your custom nlay in a -# location other than the default and have an alias with nnn option '-p' to -# invoke it. Remember it might break or lack new capabilities added to nlay -# in future releases. Check the file diff once in a while. -# -# Author: Arun Prakash Jana -# Email: engineerarun@gmail.com -# Homepage: https://github.com/jarun/nnn -# Copyright © 2016-2019 Arun Prakash Jana -# ############################################################################# - - -# Enable the lines below to handle file by extension -# This is provided for using a custom player for specific files -# $ext holds the extension -</dev/null 2>&1 &" - ">/dev/null 2>&1 &") - -#------------------ LOCKER ------------------ -elif [ "$2" == "locker" ]; then - app=("vlock" - "bashlock" - "lock") - -for index in ${!app[@]} -do - type -P ${app[$index]} &>/dev/null && - eval ${app[$index]} && - exit 0 -done - -#------------------ SCRIPT ------------------ -elif [ "$2" == "script" ]; then - # add commands or a custom script below - - # echo "my commands or custom script" - # sh "path_to_script.sh" - $SHELL "$1" - - exit 0 -fi - -#----------------- RUN APP ------------------ -for index in ${!app[@]} -do - type -P ${app[$index]} &>/dev/null && - eval ${app[$index]} ${opts[$index]} "\"$1\"" ${bg[$index]} && - break -done diff --git a/misc/nlay/nlay.1 b/misc/nlay/nlay.1 deleted file mode 100644 index 0a6a7b55..00000000 --- a/misc/nlay/nlay.1 +++ /dev/null @@ -1,37 +0,0 @@ -.Dd Mar 14, 2018 -.Dt NLAY 1 -.Os -.Sh NAME -.Nm nlay -.Nd a bash script to play files in different apps by file type or run some actions. -.Sh SYNOPSIS -.Nm -file/path type/action -.Sh DESCRIPTION -.Nm -is shipped with \fInnn\fR to deliver a level of flexibility to users to choose their own apps when running some actions, run some commands or custom scripts. It has provisions to handle text files too. However, the capability is not used in the latest releases. Now -.Nm -is invoked to run a desktop search (\fIgnome-search-tool\fR or \fIcatfish\fR) or screen locker (\fIvlock\fR or \fIbashlock\fR or \fIlock\fR) utility. However, -.Nm -can run independently and can be highly customized for personal usage. -.Pp -.Nm -supports the following options: -.Pp -"file/path" - The first argument can be the file or path to pass as an argument to the app. It can also be an empty string e.g., while locking the terminal. -.Pp -"type/action" - This can be any of the strings \fItext\fR, \fIsearch\fR, \fIscript\fR or \fIlocker\fR. -.Sh USAGE -.Pp -.Bd -literal -$ nlay info.txt text -$ nlay . search -$ nlay ~/script.sh script -$ nlay "" locker -.Ed -.Sh AUTHOR -.An Arun Prakash Jana Aq Mt engineerarun@gmail.com . -.Sh HOME -.Em https://github.com/jarun/nnn diff --git a/misc/nplay/README.md b/misc/nplay/README.md new file mode 100644 index 00000000..a18e2857 --- /dev/null +++ b/misc/nplay/README.md @@ -0,0 +1,26 @@ +## nplay + +`nplay` (*NnnPLAY*) is a customizable file opener by file extension or mime type. + +It is customizable and written for the CLI mode by default. To set GUI mode and use GUI apps by change the line + + GUI=0 +to + + GUI=1 + +### Usage + + nplay filepath + +### Integration with `nnn` + +1. Export the required config: + + export NNN_OPENER=/path/to/nplay + # Otherwise, if nplay is in $PATH + export NNN_OPENER=nplay +2. Run `nnn` with the program option to indicate a CLI opener + + nnn -c +3. `nplay` can use `nnn` plugins (e.g. mocplay is used for audio), $PATH is updated to include `nnn` plugins dir. diff --git a/misc/nplay/nplay b/misc/nplay/nplay new file mode 100755 index 00000000..d3447cdf --- /dev/null +++ b/misc/nplay/nplay @@ -0,0 +1,423 @@ +#!/usr/bin/env sh + +# ############################################################################# +# nplay: a sample script to play files in different apps by file type and mime +# shell: POSIX compliant +# usage: nplay filepath +# +# nnn integration: +# 1. Export the required config: +# export NNN_OPENER=/path/to/nplay +# # Otherwise, if nplay is in $PATH +# export NNN_OPENER=nplay +# 2. Run nnn with the program option to indicate a CLI opener +# nnn -c +# 3. nplay can use nnn plugins (e.g. mocplay is used for audio), $PATH is updated. +# +# details: +# Inspired by ranger's scope.sh, modified for usage with nnn. +# +# Tries to play 'file' (1st argument) in the following order: +# i. by extension +# ii. by mime (image, video, audio, pdf) +# iii. by mime (other file types) +# +# modification tips: +# 1. Invokes CLI utilities by default. Set GUI to 1 to enable GUI apps. +# 2. PAGER is "less -R". +# 3. Start GUI apps in bg to unblock. Redirect stdout and strerr if required. +# 4. Some CLI utilities are piped to the $PAGER, to wait and quit uniformly. +# 5. If the output cannot be paged use "read -r _" to wait for user input. +# 6. On a DE, try 'xdg-open' in handle_fallback() as last resort. +# +# Feel free to change the utilities to your favourites and add more mimes. +# +# defaults: +# By extension (only the enbaled ones): +# most archives: list with atool, bsdtar +# rar: list with unrar +# 7-zip: list with 7z +# pdf: zathura (GUI), pdftotext, mutool, exiftool +# m4a: mocplay (nnn plugin using MOC), mpv, mediainfo, exiftool +# torrent: transmission-show +# odt|ods|odp|sxw: odt2txt +# htm|html|xhtml: w3m, lynx, elinks +# json: jq, python (json.tool module) +# Multimedia by mime: +# image/*: sxiv (GUI), viu, img2txt, exiftool +# video/*: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool +# audio/*: mocplay (nnn plugin using MOC), mpv, mediainfo, exiftool +# application/pdf: zathura (GUI), pdftotext, mutool, exiftool +# Other mimes: +# text/* | */xml: vi +# image/vnd.djvu): djvutxt, exiftool +# +# ToDo: +# 1. Adapt, test and enable all mimes +# 2. Clean-up unnecessary the exit codes +# ############################################################################# + +# set to 1 to enable GUI apps +GUI=0 + +set -euf -o noclobber -o noglob -o nounset +IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n + +PATH=$PATH:"${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins" +IMAGE_CACHE_PATH="$(dirname "$1")"/.thumbs + +FPATH="$1" +FNAME=$(basename "$1") +ext="${FNAME##*.}" +if ! [ -z "$ext" ]; then + ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" +fi + +# handle this extension and exit +handle_extension() { + case "${ext}" in + ## Archive + a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ + rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip) + if which atool >/dev/null 2>&1; then + atool --list -- "${FPATH}" | less -R + exit 0 + elif which bsdtar >/dev/null 2>&1; then + bsdtar --list --file "${FPATH}" | less -R + exit 0 + fi + exit 1;; + rar) + if which unrar >/dev/null 2>&1; then + ## Avoid password prompt by providing empty password + unrar lt -p- -- "${FPATH}" | less -R + fi + exit 1;; + 7z) + if which 7z >/dev/null 2>&1; then + ## Avoid password prompt by providing empty password + 7z l -p -- "${FPATH}" | less -R + exit 0 + fi + exit 1;; + + ## PDF + pdf) + if [ $GUI -ne 0 ] && which zathura >/dev/null 2>&1; then + zathura "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which pdftotext >/dev/null 2>&1; then + ## Preview as text conversion + pdftotext -l 10 -nopgbrk -q -- "${FPATH}" - | less -R + exit 0 + elif which mutool >/dev/null 2>&1; then + mutool draw -F txt -i -- "${FPATH}" 1-10 + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}" | less -R + exit 0 + fi + exit 1;; + + ## M4A audio + m4a) + if which mocp >/dev/null 2>&1; then + mocplay "${FPATH}" >/dev/null 2>&1 + exit 0 + elif which mpv >/dev/null 2>&1; then + mpv "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which mediainfo >/dev/null 2>&1; then + mediainfo "${FPATH}" | less -R + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}"| less -R + exit 0 + fi + exit 1;; + + ## BitTorrent + torrent) + if which transmission-show >/dev/null 2>&1; then + transmission-show -- "${FPATH}" + exit 0 + fi + exit 1;; + + ## OpenDocument + odt|ods|odp|sxw) + if which odt2txt >/dev/null 2>&1; then + ## Preview as text conversion + odt2txt "${FPATH}" | less -R + exit 0 + fi + exit 1;; + + ## HTML + htm|html|xhtml) + ## Preview as text conversion + if which w3m >/dev/null 2>&1; then + w3m -dump "${FPATH}" | less -R + exit 0 + elif which lynx >/dev/null 2>&1; then + lynx -dump -- "${FPATH}" | less -R + exit 0 + elif which elinks >/dev/null 2>&1; then + elinks -dump "${FPATH}" | less -R + exit 0 + fi + ;; + ## JSON + json) + if which jq >/dev/null 2>&1; then + jq --color-output . "${FPATH}" | less -R + exit 0 + elif which python >/dev/null 2>&1; then + python -m json.tool -- "${FPATH}" | less -R + exit 0 + fi + ;; + esac +} + +handle_multimedia() { + ## Size of the preview if there are multiple options or it has to be + ## rendered from vector graphics. If the conversion program allows + ## specifying only one dimension while keeping the aspect ratio, the width + ## will be used. + # local DEFAULT_SIZE="1920x1080" + + mimetype="${1}" + case "${mimetype}" in + ## SVG + # image/svg+xml|image/svg) + # convert -- "${FPATH}" "${IMAGE_CACHE_PATH}" && exit 6 + # exit 1;; + + ## DjVu + # image/vnd.djvu) + # ddjvu -format=tiff -quality=90 -page=1 -size="${DEFAULT_SIZE}" \ + # - "${IMAGE_CACHE_PATH}" < "${FPATH}" \ + # && exit 6 || exit 1;; + + ## Image + image/*) + if [ $GUI -ne 0 ] && which sxiv >/dev/null 2>&1; then + sxiv -q "$1" "$(dirname "${FPATH}")" & + exit 0 + elif which viu >/dev/null 2>&1; then + viu -n "${FPATH}" | less -R + exit 0 + elif which img2txt >/dev/null 2>&1; then + img2txt --gamma=0.6 -- "${FPATH}" | less -R + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}" | less -R + exit 0 + fi + # local orientation + # orientation="$( identify -format '%[EXIF:Orientation]\n' -- "${FPATH}" )" + ## If orientation data is present and the image actually + ## needs rotating ("1" means no rotation)... + # if [[ -n "$orientation" && "$orientation" != 1 ]]; then + ## ...auto-rotate the image according to the EXIF data. + # convert -- "${FPATH}" -auto-orient "${IMAGE_CACHE_PATH}" && exit 6 + # fi + + ## `w3mimgdisplay` will be called for all images (unless overriden + ## as above), but might fail for unsupported types. + exit 7;; + + ## Video + video/*) + if [ $GUI -ne 0 ] && which smplayer >/dev/null 2>&1; then + smplayer "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif [ $GUI -ne 0 ] && which mpv >/dev/null 2>&1; then + mpv "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which ffmpegthumbnailer >/dev/null 2>&1; then + # Thumbnail + [ -d "${IMAGE_CACHE_PATH}" ] || mkdir "${IMAGE_CACHE_PATH}" + ffmpegthumbnailer -i "${FPATH}" -o "${IMAGE_CACHE_PATH}/${FNAME}.jpg" -s 0 + viu -n "${IMAGE_CACHE_PATH}/${FNAME}.jpg" | less -R + exit 0 + elif which mediainfo >/dev/null 2>&1; then + mediainfo "${FPATH}" | less -R + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}"| less -R + exit 0 + fi + exit 1;; + + ## Audio + audio/*) + if which mocp >/dev/null 2>&1; then + mocplay "${FPATH}" >/dev/null 2>&1 + exit 0 + elif which mpv >/dev/null 2>&1; then + mpv "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which mediainfo >/dev/null 2>&1; then + mediainfo "${FPATH}" | less -R + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}"| less -R + exit 0 + fi + exit 1;; + + ## PDF + application/pdf) + if [ $GUI -ne 0 ] && which zathura >/dev/null 2>&1; then + zathura "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which pdftotext >/dev/null 2>&1; then + ## Preview as text conversion + pdftotext -l 10 -nopgbrk -q -- "${FPATH}" - | less -R + exit 0 + elif which mutool >/dev/null 2>&1; then + mutool draw -F txt -i -- "${FPATH}" 1-10 | less -R + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}" | less -R + exit 0 + fi + exit 1;; + + # pdftoppm -f 1 -l 1 \ + # -scale-to-x "${DEFAULT_SIZE%x*}" \ + # -scale-to-y -1 \ + # -singlefile \ + # -jpeg -tiffcompression jpeg \ + # -- "${FPATH}" "${IMAGE_CACHE_PATH%.*}" \ + # && exit 6 || exit 1;; + + + ## ePub, MOBI, FB2 (using Calibre) + # application/epub+zip|application/x-mobipocket-ebook|\ + # application/x-fictionbook+xml) + # # ePub (using https://github.com/marianosimone/epub-thumbnailer) + # epub-thumbnailer "${FPATH}" "${IMAGE_CACHE_PATH}" \ + # "${DEFAULT_SIZE%x*}" && exit 6 + # ebook-meta --get-cover="${IMAGE_CACHE_PATH}" -- "${FPATH}" \ + # >/dev/null && exit 6 + # exit 1;; + + ## Font + # application/font*|application/*opentype) + # preview_png="/tmp/$(basename "${IMAGE_CACHE_PATH%.*}").png" + # if fontimage -o "${preview_png}" \ + # --pixelsize "120" \ + # --fontname \ + # --pixelsize "80" \ + # --text " ABCDEFGHIJKLMNOPQRSTUVWXYZ " \ + # --text " abcdefghijklmnopqrstuvwxyz " \ + # --text " 0123456789.:,;(*!?') ff fl fi ffi ffl " \ + # --text " The quick brown fox jumps over the lazy dog. " \ + # "${FPATH}"; + # then + # convert -- "${preview_png}" "${IMAGE_CACHE_PATH}" \ + # && rm "${preview_png}" \ + # && exit 6 + # else + # exit 1 + # fi + # ;; + + ## Preview archives using the first image inside. + ## (Very useful for comic book collections for example.) + # application/zip|application/x-rar|application/x-7z-compressed|\ + # application/x-xz|application/x-bzip2|application/x-gzip|application/x-tar) + # local fn=""; local fe="" + # local zip=""; local rar=""; local tar=""; local bsd="" + # case "${mimetype}" in + # application/zip) zip=1 ;; + # application/x-rar) rar=1 ;; + # application/x-7z-compressed) ;; + # *) tar=1 ;; + # esac + # { [ "$tar" ] && fn=$(tar --list --file "${FPATH}"); } || \ + # { fn=$(bsdtar --list --file "${FPATH}") && bsd=1 && tar=""; } || \ + # { [ "$rar" ] && fn=$(unrar lb -p- -- "${FPATH}"); } || \ + # { [ "$zip" ] && fn=$(zipinfo -1 -- "${FPATH}"); } || return + # + # fn=$(echo "$fn" | python -c "import sys; import mimetypes as m; \ + # [ print(l, end='') for l in sys.stdin if \ + # (m.guess_type(l[:-1])[0] or '').startswith('image/') ]" |\ + # sort -V | head -n 1) + # [ "$fn" = "" ] && return + # [ "$bsd" ] && fn=$(printf '%b' "$fn") + # + # [ "$tar" ] && tar --extract --to-stdout \ + # --file "${FPATH}" -- "$fn" > "${IMAGE_CACHE_PATH}" && exit 6 + # fe=$(echo -n "$fn" | sed 's/[][*?\]/\\\0/g') + # [ "$bsd" ] && bsdtar --extract --to-stdout \ + # --file "${FPATH}" -- "$fe" > "${IMAGE_CACHE_PATH}" && exit 6 + # [ "$bsd" ] || [ "$tar" ] && rm -- "${IMAGE_CACHE_PATH}" + # [ "$rar" ] && unrar p -p- -inul -- "${FPATH}" "$fn" > \ + # "${IMAGE_CACHE_PATH}" && exit 6 + # [ "$zip" ] && unzip -pP "" -- "${FPATH}" "$fe" > \ + # "${IMAGE_CACHE_PATH}" && exit 6 + # [ "$rar" ] || [ "$zip" ] && rm -- "${IMAGE_CACHE_PATH}" + # ;; + esac +} + +handle_mime() { + mimetype="${1}" + case "${mimetype}" in + ## Text + text/* | */xml) + vi "${FPATH}" + exit 1;; + ## Syntax highlight + # if [[ "$( stat --printf='%s' -- "${FPATH}" )" -gt "${HIGHLIGHT_SIZE_MAX}" ]]; then + # exit 2 + # fi + # if [[ "$( tput colors )" -ge 256 ]]; then + # local pygmentize_format='terminal256' + # local highlight_format='xterm256' + # else + # local pygmentize_format='terminal' + # local highlight_format='ansi' + # fi + # env HIGHLIGHT_OPTIONS="${HIGHLIGHT_OPTIONS}" highlight \ + # --out-format="${highlight_format}" \ + # --force -- "${FPATH}" && exit 5 + # pygmentize -f "${pygmentize_format}" -O "style=${PYGMENTIZE_STYLE}"\ + # -- "${FPATH}" && exit 5 + # exit 2;; + + ## DjVu + image/vnd.djvu) + if which djvutxt >/dev/null 2>&1; then + ## Preview as text conversion (requires djvulibre) + djvutxt "${FPATH}" | less -R + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}" | less -R + exit 0 + fi + exit 1;; + esac +} + +handle_fallback() { + if [ $GUI -ne 0 ]; then + xdg-open "${FPATH}" >/dev/null 2>&1 & + exit 0 + fi + + echo '----- File details -----' && file --dereference --brief -- "${FPATH}" + exit 1 +} + +MIMETYPE="$( file --dereference --brief --mime-type -- "${FPATH}" )" +handle_extension +handle_multimedia "${MIMETYPE}" +handle_mime "${MIMETYPE}" +handle_fallback + +exit 1 diff --git a/nnn.1 b/nnn.1 index 630fd589..976ecbf4 100644 --- a/nnn.1 +++ b/nnn.1 @@ -232,7 +232,9 @@ when dealing with the !, e and p commands respectively. A single combination to .Pp \fBNNN_OPENER:\fR specify a custom file opener. .Bd -literal - export NNN_OPENER=mimeopen + export NNN_OPENER=nplay + + nplay source: https://github.com/jarun/nnn/blob/master/misc/nplay/nplay .Ed .Pp \fBNNN_IDLE_TIMEOUT:\fR set idle timeout (in seconds) to invoke terminal locker (default: disabled).