From 00aaee9ff16fba929b8ec629810640b567d78591 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Sun, 23 Apr 2017 23:12:54 +0530 Subject: [PATCH] nlay - a highly customizable file handler --- Makefile | 11 +++-- Makefile.generic | 11 +++-- README.md | 35 ++++++++++---- config.def.h | 17 +++---- nlay | 119 +++++++++++++++++++++++++++++++++++++++++++++++ nnn.1 | 33 ++++++------- nnn.c | 95 ++++++++++++++++++------------------- 7 files changed, 225 insertions(+), 96 deletions(-) create mode 100755 nlay diff --git a/Makefile b/Makefile index bb450f91..457da0c6 100644 --- a/Makefile +++ b/Makefile @@ -6,17 +6,18 @@ MANPREFIX = $(PREFIX)/share/man CFLAGS += -O3 -march=native -Wall -Wextra -Wno-unused-parameter LDLIBS = -lreadline ifeq ($(shell uname), Darwin) - LDLIBS += -lncurses + LDLIBS += -lncurses else - LDLIBS += -lncursesw + LDLIBS += -lncursesw endif -DISTFILES = nnn.c config.def.h nnn.1 Makefile README.md LICENSE +DISTFILES = nlay nnn.c config.def.h nnn.1 Makefile README.md LICENSE LOCALCONFIG = config.h SRC = nnn.c BIN = nnn +PLAYER = nlay -all: $(BIN) +all: $(BIN) $(PLAYER) $(LOCALCONFIG): config.def.h cp config.def.h $@ @@ -30,11 +31,13 @@ $(BIN): $(SRC) install: all mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + cp -f $(PLAYER) $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(MANPREFIX)/man1 cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) + rm -f $(DESTDIR)$(PREFIX)/bin/$(PLAYER) rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 dist: diff --git a/Makefile.generic b/Makefile.generic index 075ba0ce..c4e8fdd0 100644 --- a/Makefile.generic +++ b/Makefile.generic @@ -6,17 +6,18 @@ MANPREFIX = $(PREFIX)/share/man CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter LDLIBS = -lreadline ifeq ($(shell uname), Darwin) - LDLIBS += -lncurses + LDLIBS += -lncurses else - LDLIBS += -lncursesw + LDLIBS += -lncursesw endif -DISTFILES = nnn.c config.def.h nnn.1 Makefile README.md LICENSE +DISTFILES = nlay nnn.c config.def.h nnn.1 Makefile README.md LICENSE LOCALCONFIG = config.h SRC = nnn.c BIN = nnn +PLAYER = nlay -all: $(BIN) +all: $(BIN) $(PLAYER) $(LOCALCONFIG): config.def.h cp config.def.h $@ @@ -30,11 +31,13 @@ $(BIN): $(SRC) install: all mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + cp -f $(PLAYER) $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(MANPREFIX)/man1 cp -f $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) + rm -f $(DESTDIR)$(PREFIX)/bin/$(PLAYER) rm -f $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 dist: diff --git a/README.md b/README.md index ba253e52..93cbb90e 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Noice is Not Noice, a noicer fork... - [Introduction](#introduction) - [Features](#features) - [Performance](#performance) +- [nlay](#nlay) - [Installation](#installation) - [Usage](#usage) - [Cmdline options](#cmdline-options) @@ -55,6 +56,7 @@ Have fun with it! PRs are welcome. Check out [#1](https://github.com/jarun/nnn/i - Super-easy navigation with roll-over at edges - Jump HOME or back to the last visited directory (as you normally do!) - Desktop opener integration to handle mime types +- Customizable bash script nlay to handle known file types - Disk usage analyzer mode - Basic and detail views - Show stat and file information @@ -88,6 +90,10 @@ nnn vs. mc vs. ranger memory usage while viewing a directory with 10,178 files, 28863 vaio 20 0 19876 6436 2620 S 0.0 0.1 0:00.19 nnn -d ``` +### nlay + +nnn comes with an easily customizable bash shell script to handle files - nlay. To know more about it, visit [nlay on wiki](https://github.com/jarun/nnn/wiki/all-about-nlay). + ### Installation nnn needs libreadline, libncursesw (on Linux or ncurses on OS X) and standard libc. @@ -180,22 +186,29 @@ The following abbreviations are used in the detail view: | `c` | Character Device | #### File handling + +nnn is designed to play files using multiple strategies (in order of decreasing priority): - Set `NNN_OPENER` to let a desktop opener handle it all. E.g.: export NNN_OPENER=xdg-open export NNN_OPENER="gio open" export NNN_OPENER=gvfs-open - - Selective file associations (ignored if `NNN_OPENER` is set): - - vi - plain text files (determined using file) - - mpv - common audio and video mimes - - [zathura](https://pwmt.org/projects/zathura/) - pdf files - - to customize further see [how to change file associations](#change-file-associations) - - Set `NNN_FALLBACK_OPENER` as the fallback opener: - - if the executable in static file association is missing - - if a file type was not handled in static file association + - If nnn recognizes the file extension, it invokes nlay (which invokes the players). Default players: + - mpv - audio and video + - viewnior - image + - [zathura](https://pwmt.org/projects/zathura/) - pdf + - vim - plain text + - to add, remove recognized extensions in nnn, see [how to change file associations](#change-file-associations) + - If a file without any extension is a plain text file, it is opened in vi + - Set `NNN_FALLBACK_OPENER` as the fallback opener. E.g.: + + export NNN_FALLBACK_OPENER=xdg-open + export NNN_FALLBACK_OPENER="gio open" + export NNN_FALLBACK_OPENER=gvfs-open - To enable the desktop file manager key, set `NNN_DE_FILE_MANAGER`. E.g.: export NNN_DE_FILE_MANAGER=thunar + export NNN_DE_FILE_MANAGER=nautilus - [mediainfo](https://mediaarea.net/en/MediaInfo) is required to view media information #### Help @@ -246,7 +259,11 @@ Start nnn and use `^K` to copy the absolute path (from `/`) of the file under th #### Change file associations -If you want to set custom applications for certain mime types, or change the ones set already (e.g. vi, mpv, zathura), modify the `assocs` structure in [config.def.h](https://github.com/jarun/nnn/blob/master/config.def.h) (it's easy). Then re-compile and install. +If `NNN_OPENER` is not set, nnn tries to recognize a file by the file extension and invokes nlay. To change the extensions recognized by nnn, modify the `assocs` structure in [config.def.h](https://github.com/jarun/nnn/blob/master/config.def.h) (it's easy). Then re-compile and install. + +If you want to add a file extension mainline, please raise a bug. Without it nnn will not invoke nlay. + +nlay has provisions (disabled by default) to handle a specific file extension too. However, the extension should be recognized by nnn first. ### Why fork? diff --git a/config.def.h b/config.def.h index 689aab5b..28771bf8 100644 --- a/config.def.h +++ b/config.def.h @@ -11,18 +11,15 @@ static int showhidden = 0; /* Set to 1 to show hidden files by default */ static int showdetail = 0; /* Set to show additional file info */ static char *idlecmd = "rain"; /* The screensaver program */ -struct assoc assocs[] = { - { "\\.(c|cpp|h|txt|log|sh)$", "vi" }, - { "\\.(avi|mp4|mkv|3gp|mov)$", "mpv" }, - { "\\.(wma|mp3|ogg|flac|m4a)$", "mpv" }, - { "\\.(png|jpg|gif)$", "viewnior" }, - //{ "\\.(html|svg)$", "firefox" }, - { "\\.pdf$", "zathura" }, - //{ "\\.sh$", "sh" }, - //{ ".", "less" }, +static struct assoc assocs[] = { + { "\\.(c|cpp|h|log|md|py|sh|txt)$", "text" }, + { "\\.(3g2|3gp|asf|avi|divx|flv|m2v|m4v|mkv|mov|mp4|mp4v|mpeg|mpg|ogv|qt|rm|rmvb|vob|webm|wmv)$", "video" }, + { "\\.(aac|ac3|amr|flac|m4a|m4b|m4p|mp3|mp4a|ogg|opus|ra|wav|wma)$", "audio" }, + { "\\.(bmp|gif|jpeg|jpg|pbm|pgm|png|svg|tiff|webp)$", "image" }, + { "\\.pdf$", "pdf" }, }; -struct key bindings[] = { +static struct key bindings[] = { /* Quit */ { 'q', SEL_QUIT, "", "" }, { 'Q', SEL_CDQUIT, "", "" }, diff --git a/nlay b/nlay new file mode 100755 index 00000000..fd06307a --- /dev/null +++ b/nlay @@ -0,0 +1,119 @@ +#!/bin/bash + +# ############################################################################# +# nlay: a customizable script to play files in different apps by file type +# +# usage: nlay file type +# +# 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 bg +# setting. If bg is set the app is detached and started in the background in +# silent mode. +# +# The bg setting depends on personal preference and type of app, e.g., +# I would start vim (CLI) in the foreground but Sublime Text (GUI) in the +# 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 +# to stop console based background apps. +# +# 3. Assuming you don't to play multiple audio/video files simultaneously, +# nlay kills any running instances of the audio/video player in bg mode. +# +# 4. Keep a personal backup (on GitHub Gist maybe?) of this file if you modify +# it. nlay is OVERWRITTEN during nnn upgrade. +# +# Author: Arun Prakash Jana +# Email: engineerarun@gmail.com +# ############################################################################# + + +# 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 + fi + + eval $app $audio_opts "\"$1\"" $bg + exit 0 +fi + +#------------------ VIDEO ------------------- +if [ "$2" == "video" ]; then + app=mpv + # To start mpv in a window enable video_opts + #video_opts="--no-terminal --force-window" + + #bg=">/dev/null 2>&1 &" + + if [ -n "$bg" ]; then + killall -9 $app >/dev/null 2>&1 + fi + + eval $app $video_opts "\"$1\"" $bg + exit 0 +fi + +#------------------ IMAGE ------------------- +if [ "$2" == "image" ]; then + app=viewnior + #image_opts= + + bg=">/dev/null 2>&1 &" + + eval $app $image_opts "\"$1\"" $bg + exit 0 +fi + +#------------------- PDF -------------------- +if [ "$2" == "pdf" ]; then + app=zathura + #pdf_opts= + + bg=">/dev/null 2>&1 &" + + eval $app $pdf_opts "\"$1\"" $bg + exit 0 +fi + +#---------------- PLAINTEXT ----------------- +if [ "$2" == "text" ]; then + app=vim + #txt_opts= + + #bg=">/dev/null 2>&1 &" + + eval $app $txt_opts "\"$1\"" $bg + exit 0 +fi diff --git a/nnn.1 b/nnn.1 index 8b74150a..4fefe86b 100644 --- a/nnn.1 +++ b/nnn.1 @@ -106,9 +106,22 @@ supports the following options: show program help and exit .Sh CONFIGURATION .Nm +invokes +.Pa nlay +to play a file if it recognizes a file by extension. +.Pa nlay +is a +highly customizable bash shell script which invokes a player depending on the +type of file. Read more on +.Pa nlay +at: +.br +.Em https://github.com/jarun/nnn/wiki/all-about-nlay +.Pp +.Nm is configured by modifying -.Pa config.h -and recompiling the code. +.Pa config.def.h +and recompiling the code. config.h is generated as a backup of config.def.h. .Pp See the environment and examples sections below for more options and information. .Pp @@ -154,22 +167,6 @@ type. Custom associations are listed in the EXAMPLES section below. echo -n $1 | xsel --clipboard --input ------------------------------------- .Ed -.Sh EXAMPLES -The following example shows one possible configuration for -file associations which is also the default if environment -variable NNN_OPENER is not set: -.Bd -literal - ----------------------------------------------- - struct assoc assocs[] = { - { "\\.(c|cpp|h|txt|log|sh)$", "vi" }, - { "\\.(wma|mp3|ogg|flac)$", "mpv" }, - { "\\.pdf$", "zathura" }, - }; - ----------------------------------------------- -Plain text files are opened with vi. -.br -Any other file types are opened with the 'xdg-open' command. -.Ed .Sh KNOWN ISSUES If you are using urxvt you might have to set backspacekey to DEC. .Sh AUTHORS diff --git a/nnn.c b/nnn.c index 8c9029ea..37a1938c 100644 --- a/nnn.c +++ b/nnn.c @@ -68,7 +68,7 @@ xprintf(int fd, const char *fmt, ...) struct assoc { char *regex; /* Regex to match on filename */ - char *bin; /* Program */ + char *mime; /* File type */ }; /* Supported actions */ @@ -134,7 +134,7 @@ static struct entry *dents; static int ndents, cur, total_dents; static int idle; static char *opener; -static char *fallback_opener; +static char *fb_opener; static char *copier; static char *desktop_manager; static off_t blk_size; @@ -384,23 +384,20 @@ strstrip(char *s) } static char * -openwith(char *file) +getmime(char *file) { regex_t regex; - char *bin = NULL; unsigned int i; + static unsigned int len = LEN(assocs); - for (i = 0; i < LEN(assocs); i++) { + for (i = 0; i < len; i++) { if (regcomp(®ex, assocs[i].regex, REG_NOSUB | REG_EXTENDED | REG_ICASE) != 0) continue; - if (regexec(®ex, file, 0, NULL, 0) == 0) { - bin = assocs[i].bin; - break; - } + if (regexec(®ex, file, 0, NULL, 0) == 0) + return assocs[i].mime; } - DPRINTF_S(bin); - return bin; + return NULL; } static int @@ -540,6 +537,7 @@ nextsel(char **run, char **env) { int c; unsigned int i; + static unsigned int len = LEN(bindings); c = getch(); if (c == -1) @@ -547,7 +545,7 @@ nextsel(char **run, char **env) else idle = 0; - for (i = 0; i < LEN(bindings); i++) + for (i = 0; i < len; i++) if (c == bindings[i].sym) { *run = bindings[i].run; *env = bindings[i].env; @@ -1248,7 +1246,7 @@ browse(char *ipath, char *ifilter) static char path[PATH_MAX], oldpath[PATH_MAX], newpath[PATH_MAX]; static char lastdir[PATH_MAX]; static char fltr[LINE_MAX]; - char *bin, *dir, *tmp, *run, *env; + char *mime, *dir, *tmp, *run, *env; struct stat sb; regex_t re; int r, fd; @@ -1352,9 +1350,8 @@ nochange: case S_IFREG: { static char cmd[MAX_CMD_LEN]; - static char *runvi = "vi"; - /* If default mime opener is set, use it */ + /* If NNN_OPENER is set, use it */ if (opener) { snprintf(cmd, MAX_CMD_LEN, "%s \"%s\" > /dev/null 2>&1", @@ -1363,42 +1360,38 @@ nochange: continue; } - /* Try custom applications */ - bin = openwith(newpath); - - /* If custom app doesn't exist try fallback */ - snprintf(cmd, MAX_CMD_LEN, "which \"%s\"", bin); - if (get_output(cmd, MAX_CMD_LEN) == NULL) - bin = NULL; - - if (bin == NULL) { - /* If a custom handler application is - not set, open plain text files with - vi, then try fallback_opener */ - snprintf(cmd, MAX_CMD_LEN, - "file \"%s\"", newpath); - if (get_output(cmd, MAX_CMD_LEN) == NULL) - goto nochange; - - if (strstr(cmd, "ASCII text") != NULL) - bin = runvi; - else if (fallback_opener) { - snprintf(cmd, MAX_CMD_LEN, - "%s \"%s\" > \ - /dev/null 2>&1", - fallback_opener, - newpath); - r = system(cmd); - continue; - } else { - printmsg("No association"); - goto nochange; - } + /* Play with nlay if identified */ + mime = getmime(dents[cur].name); + if (mime) { + snprintf(cmd, MAX_CMD_LEN, "nlay \"%s\" %s", + newpath, mime); + exitcurses(); + r = system(cmd); + initcurses(); + continue; } - exitcurses(); - spawn(bin, newpath, NULL, 0); - initcurses(); - continue; + + /* If nlay doesn't handle it, open plain text + files with vi, then try NNN_FALLBACK_OPENER */ + snprintf(cmd, MAX_CMD_LEN, + "file \"%s\"", newpath); + if (get_output(cmd, MAX_CMD_LEN) == NULL) + continue; + + if (strstr(cmd, "ASCII text") != NULL) { + exitcurses(); + spawn("vi", newpath, NULL, 0); + initcurses(); + continue; + } else if (fb_opener) { + snprintf(cmd, MAX_CMD_LEN, "%s \"%s\" > /dev/null 2>&1", + fb_opener, newpath); + r = system(cmd); + continue; + } + + printmsg("No association"); + goto nochange; } default: printmsg("Unsupported file"); @@ -1788,7 +1781,7 @@ main(int argc, char *argv[]) opener = getenv("NNN_OPENER"); /* Get the fallback desktop mime opener, if set */ - fallback_opener = getenv("NNN_FALLBACK_OPENER"); + fb_opener = getenv("NNN_FALLBACK_OPENER"); /* Get the desktop file browser, if set */ desktop_manager = getenv("NNN_DE_FILE_MANAGER");