diff options
| author | --global <ik.boikov.public@protonmail.com> | 2021-07-14 23:02:24 +0200 |
|---|---|---|
| committer | --global <ik.boikov.public@protonmail.com> | 2021-07-14 23:02:24 +0200 |
| commit | fd850fb2ed9870edcb1df205e08e8ccb25fb6760 (patch) | |
| tree | 641b5af724d86a0a383b2c580dd838b03c1cc42c /nnn | |
| parent | 72000d2f0c23a685f4c4d458158712340f6b9a68 (diff) | |
more nnn plugins, more luke's scripts
Diffstat (limited to 'nnn')
58 files changed, 3621 insertions, 12 deletions
diff --git a/nnn/.config/nnn/plugins/.cbcp b/nnn/.config/nnn/plugins/.cbcp new file mode 100755 index 0000000..04a09dc --- /dev/null +++ b/nnn/.config/nnn/plugins/.cbcp @@ -0,0 +1,50 @@ +#!/usr/bin/env sh + +# Description: Copy selection to system clipboard as newline-separated entries +# Dependencies: +# - tr +# - xclip/xsel (Linux) +# - pbcopy (macOS) +# - termux-clipboard-set (Termux) +# - clip.exe (WSL) +# - clip (Cygwin) +# - wl-copy (Wayland) +# - clipboard (Haiku) +# +# Limitation: breaks if a filename has newline in it +# +# Note: For a space-separated list: +# xargs -0 < "$SELECTION" +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +if which xsel >/dev/null 2>&1; then + # Linux + tr '\0' '\n' < "$selection" | xsel -bi +elif which xclip >/dev/null 2>&1; then + # Linux + tr '\0' '\n' < "$selection" | xclip -sel clip +elif which pbcopy >/dev/null 2>&1; then + # macOS + tr '\0' '\n' < "$selection" | pbcopy +elif which termux-clipboard-set >/dev/null 2>&1; then + # Termux + tr '\0' '\n' < "$selection" | termux-clipboard-set +elif which clip.exe >/dev/null 2>&1; then + # WSL + tr '\0' '\n' < "$selection" | clip.exe +elif which clip >/dev/null 2>&1; then + # Cygwin + tr '\0' '\n' < "$selection" | clip +elif which wl-copy >/dev/null 2>&1; then + # Wayland + tr '\0' '\n' < "$selection" | wl-copy +elif which clipboard >/dev/null 2>&1; then + # Haiku + tr '\0' '\n' < "$selection" | clipboard --stdin +fi diff --git a/nnn/.config/nnn/plugins/.nmv b/nnn/.config/nnn/plugins/.nmv new file mode 100755 index 0000000..7762546 --- /dev/null +++ b/nnn/.config/nnn/plugins/.nmv @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +# Description: An almost fully POSIX compliant batch file renamer +# +# Note: nnn auto-detects and invokes this plugin if available +# Whitespace is used as delimiter for read. +# The plugin doesn't support filenames with leading or trailing whitespace +# +# Capabilities: +# 1. Basic file rename +# 2. Detects order change +# 3. Can move files +# 4. Can remove files +# 5. Switch number pairs to swap filenames +# +# Shell: bash +# Author: KlzXS + +EDITOR="${EDITOR:-vi}" +TMPDIR="${TMPDIR:-/tmp}" +INCLUDE_HIDDEN="${INCLUDE_HIDDEN:-0}" +VERBOSE="${VERBOSE:-0}" + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} +exit_status=0 + +dst_file=$(mktemp "$TMPDIR/.nnnXXXXXX") + +if [ -s "$selection" ]; then + printf "Rename 'c'urrent / 's'election? " + read -r resp + + if ! [ "$resp" = "c" ] && ! [ "$resp" = "s" ]; then + exit 1 + fi +fi + +if [ "$resp" = "s" ]; then + arr=$(tr '\0' '\n' < "$selection") +else + if [ "$INCLUDE_HIDDEN" -eq 0 ]; then + arr=$(find . ! -name . -prune ! -name ".*" -print | sort) + else + arr=$(find . ! -name . -prune -print | sort) + fi +fi + +lines=$(printf "%s\n" "$arr" | wc -l) +width=${#lines} + +printf "%s" "$arr" | awk '{printf("%'"${width}"'d %s\n", NR, $0)}' > "$dst_file" + +items=("~") +while IFS='' read -r line; do + items+=("$line"); +done < <(printf "%s\n" "$arr") + +$EDITOR "$dst_file" + +while read -r num name; do + if [ -z "$name" ]; then + if [ -z "$num" ]; then + continue + fi + + printf "%s: unable to parse line, aborting\n" "$0" + exit 1 + fi + + # check if $num is an integer + if [ ! "$num" -eq "$num" ] 2> /dev/null; then + printf "%s: unable to parse line, aborting\n" "$0" + exit 1 + fi + + src=${items[$num]} + + if [ -z "$src" ]; then + printf "%s: unknown item number %s\n" "$0" "$num" > /dev/stderr + continue + elif [ "$name" != "$src" ]; then + if [ -z "$name" ]; then + continue + fi + + if [ ! -e "$src" ] && [ ! -L "$src" ]; then + printf "%s: %s does not exit\n" "$0" "$src" > /dev/stderr + + unset "items[$num]" + continue + fi + + # handle swaps + if [ -e "$name" ] || [ -L "$name" ]; then + tmp="$name~" + c=0 + + while [ -e "$tmp" ] || [ -L "$tmp" ]; do + c=$((c+1)) + tmp="$tmp~$c" + done + + if mv "$name" "$tmp"; then + if [ "$VERBOSE" -ne 0 ]; then + printf "'%s' -> '%s'\n" "$name" "$tmp" + fi + else + printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr + exit_status=1 + fi + + for key in "${!items[@]}"; do + if [ "${items[$key]}" = "$name" ]; then + items[$key]="$tmp" + fi + done + fi + + dir=$(dirname "$name") + if [ ! -d "$dir" ] && ! mkdir -p "$dir"; then + printf "%s: failed to create directory tree %s\n" "$0" "$dir" > /dev/stderr + exit_status=1 + elif ! mv "$src" "$name"; then + printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr + exit_status=1 + else + if [ -d "$name" ]; then + for key in "${!items[@]}"; do + items[$key]=$(printf "%s" "${items[$key]}" | sed "s|^$src\(\$\|\/\)|$name\1|") + done + + if [ "$VERBOSE" -ne 0 ]; then + printf "'%s' => '%s'\n" "$src" "$name" + fi + else + true + if [ "$VERBOSE" -ne 0 ]; then + printf "'%s' -> '%s'\n" "$src" "$name" + fi + fi + fi + fi + + unset "items[$num]" +done <"$dst_file" + +unset "items[0]" +for item in "${items[@]}"; do + rm -ri "$item" +done + +rm "$dst_file" +exit $exit_status diff --git a/nnn/.config/nnn/plugins/.nnn-plugin-helper b/nnn/.config/nnn/plugins/.nnn-plugin-helper index a0377ac..bcb9649 100755 --- a/nnn/.config/nnn/plugins/.nnn-plugin-helper +++ b/nnn/.config/nnn/plugins/.nnn-plugin-helper @@ -33,6 +33,6 @@ nnn_cd () { } cmd_exists () { - type "$1" > /dev/null 2>&1 - echo $? + which "$1" > /dev/null 2>&1 + echo $? } diff --git a/nnn/.config/nnn/plugins/.ntfy b/nnn/.config/nnn/plugins/.ntfy new file mode 100755 index 0000000..3a7f27f --- /dev/null +++ b/nnn/.config/nnn/plugins/.ntfy @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +# Description: Show a notification +# +# Details: nnn invokes this plugin to show notification when a cp/mv/rm operation is complete. +# +# Dependencies: notify-send (Ubuntu)/ntfy (https://github.com/dschep/ntfy)/osascript (macOS)/notify (Haiku) +# +# Shell: POSIX compliant +# Author: Anna Arad + +OS="$(uname)" + +if which notify-send >/dev/null 2>&1; then + notify-send nnn "Done!" +elif [ "$OS" = "Darwin" ]; then + osascript -e 'display notification "Done!" with title "nnn"' +elif which ntfy >/dev/null 2>&1; then + ntfy -t nnn send "Done!" +elif [ "$OS" = "Haiku" ]; then + notify --title "nnn" "Done!" +fi diff --git a/nnn/.config/nnn/plugins/autojump b/nnn/.config/nnn/plugins/autojump new file mode 100755 index 0000000..db10c84 --- /dev/null +++ b/nnn/.config/nnn/plugins/autojump @@ -0,0 +1,42 @@ +#!/usr/bin/env sh + +# Description: Navigate to directory using jump/autojump/zoxide +# +# Dependencies: jump - https://github.com/gsamokovarov/jump +# OR autojump - https://github.com/wting/autojump +# OR zoxide - https://github.com/ajeetdsouza/zoxide +# +# Note: The dependencies STORE NAVIGATION PATTERNS +# +# Shell: POSIX compliant +# Authors: Marty Buchaus, Dave Snider, Tim Adler + +if [ -z "$NNN_PIPE" ]; then + echo 'ERROR: NNN_PIPE is not set' | ${PAGER:-less} + exit 2 +fi + +if which jump >/dev/null 2>&1; then + printf "jump to : " + read -r dir + odir="$(jump cd "$dir")" + printf "%s" "0c$odir" > "$NNN_PIPE" +elif which autojump >/dev/null 2>&1; then + printf "jump to : " + read -r dir + odir="$(autojump "$dir")" + printf "%s" "0c$odir" > "$NNN_PIPE" +elif which zoxide >/dev/null 2>&1; then + if which fzf >/dev/null 2>&1; then + odir="$(zoxide query -i --)" + printf "%s" "0c$odir" > "$NNN_PIPE" + else + printf "jump to : " + read -r dir + odir="$(zoxide query -- "$dir")" + printf "%s" "0c$odir" > "$NNN_PIPE" + fi +else + printf "No supported autojump script found. (jump/autojump/zoxide are supported)" + read -r _ +fi diff --git a/nnn/.config/nnn/plugins/bookmarks b/nnn/.config/nnn/plugins/bookmarks new file mode 100755 index 0000000..9bf491e --- /dev/null +++ b/nnn/.config/nnn/plugins/bookmarks @@ -0,0 +1,61 @@ +#!/usr/bin/env sh + +# Description: Use named bookmarks using symlinks +# +# Dependencies: fzf +# +# Usage: +# 1. Create a $BOOKMARKS_DIR directory +# By default, $BOOKMARKS_DIR is set to: ${XDG_CACHE_HOME:-$HOME/.cache}/nnn/bookmarks +# +# 2. Create symlinks to directories +# `cd $BOOKMARKS_DIR` +# `ln -s /path/to/useful/directory bookmark_name` +# `ln -s $XDG_CONFIG_HOME/nnn/plugins nnn_plugins" +# `ln -s /path/to/documents docs` +# `ln -s /path/to/media media` +# `ln -s /path/to/movies movies` +# +# Bonus tip: Add `$BOOKMARKS_DIR` to your `$CDPATH` +# https://linux.101hacks.com/cd-command/cdpath/ +# +# TODO: +# 1. Remove `fzf` dependency +# +# Shell: POSIX compliant +# Author: Todd Yamakawa + +if [ -z "$BOOKMARKS_DIR" ]; then + BOOKMARKS_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/nnn/bookmarks" +fi + +# Check if NNN_PIPE is set +if [ -z "$NNN_PIPE" ]; then + echo 'ERROR: NNN_PIPE is not set' | ${PAGER:-less} + exit 2 +fi + +# Get all directory symlinks +get_links() { + for entry in "$1"/*; do + + # Skip unless directory symlink + [ -h "$entry" ] || continue + [ -d "$entry" ] || continue + + printf "%20s -> %s\n" "$(basename "$entry")" "$(readlink -f "$entry")" + done | fzf | + awk 'END { + if (length($1) == 0) { print "'"$PWD"'" } + else { print "'"$BOOKMARKS_DIR"'/"$1 } + }' +} + +# Choose symlink with fzf +cddir="$(get_links "$BOOKMARKS_DIR")" + +# Writing result to NNN_PIPE will change nnn's active directory +# https://github.com/jarun/nnn/tree/master/plugins#send-data-to-nnn +context=0 +printf "%s" "${context}c$(readlink -f "$cddir")" > "$NNN_PIPE" + diff --git a/nnn/.config/nnn/plugins/boom b/nnn/.config/nnn/plugins/boom new file mode 100755 index 0000000..5debfad --- /dev/null +++ b/nnn/.config/nnn/plugins/boom @@ -0,0 +1,47 @@ +#!/usr/bin/env sh + +# Description: Play random music from current directory. Identifies MP3, FLAC, M4A, WEBM, WMA. +# You may want to set GUIPLAYER. +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +GUIPLAYER="${GUIPLAYER}" +NUMTRACKS="${NUMTRACKS:-100}" + +if [ ! -z "$GUIPLAYER" ]; then + find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | shuf -n "$NUMTRACKS" | xargs -d "\n" "$GUIPLAYER" > /dev/null 2>&1 & + + # detach the player + sleep 1 +elif which mocp >/dev/null 2>&1; then + cmd=$(pgrep -x mocp 2>/dev/null) + ret=$cmd + + if [ -z "$ret" ]; then + # start MOC server + mocp -S + mocp -o shuffle + else + # mocp running, check if it's playing + state=$(mocp -i | grep "State:" | cut -d' ' -f2) + if [ "$state" = 'PLAY' ]; then + # add up to 100 random audio files + find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | head -n "$NUMTRACKS" | xargs -d "\n" mocp -a + exit + fi + fi + + # clear MOC playlist + mocp -c + mocp -o shuffle + + # add up to 100 random audio files + find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | head -n "$NUMTRACKS" | xargs -d "\n" mocp -a + + # start playing + mocp -p +else + printf "moc missing" + read -r _ +fi diff --git a/nnn/.config/nnn/plugins/bulknew b/nnn/.config/nnn/plugins/bulknew new file mode 100755 index 0000000..4010662 --- /dev/null +++ b/nnn/.config/nnn/plugins/bulknew @@ -0,0 +1,32 @@ +#!/usr/bin/env sh + +# Description: Allows for creation of multiple files/directories at the same time. +# Plugin opens a temp file where each entry is to be written on a separate line +# +# Note: Only relative paths are supported. Absolute paths are ignored +# Leading and trailing whitespace in path names is also ignored +# +# Shell: POSIX compliant +# Author: KlzXS + +EDITOR="${EDITOR:-vi}" +TMPDIR="${TMPDIR:-/tmp}" + +printf "'f'ile / 'd'ir? " +read -r resp + +if [ "$resp" = "f" ]; then + #shellcheck disable=SC2016 + cmd='mkdir -p "$(dirname "{}")" && touch "{}"' +elif [ "$resp" = "d" ]; then + cmd='mkdir -p {}' +else + exit 1 +fi + +tmpfile=$(mktemp "$TMPDIR/.nnnXXXXXX") +$EDITOR "$tmpfile" + +sed "/^\//d" "$tmpfile" | xargs -n1 -I{} sh -c "$cmd" + +rm "$tmpfile" diff --git a/nnn/.config/nnn/plugins/chksum b/nnn/.config/nnn/plugins/chksum new file mode 100755 index 0000000..0a386e5 --- /dev/null +++ b/nnn/.config/nnn/plugins/chksum @@ -0,0 +1,66 @@ +#!/usr/bin/env sh + +# Description: Create and verify checksums +# +# For selection: it will generate one file containing the checksums with file names +# [and with paths if they are in another directory] +# the output checksum filename will be checksum_timestamp.checksum_type +# For file: if the file is a checksum, the plugin does the verification +# if the file is not a checksum, checksum will be generated for it +# the output checksum filename will be filename.checksum_type +# For directory: recursively calculates checksum for all the files in the directory +# the output checksum filename will be directory.checksum_type +# +# Shell: POSIX compliant +# Authors: ath3, Arun Prakash Jana + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} +resp=f +chsum=md5 + +checksum_type() +{ + echo "possible checksums: md5, sha1, sha224, sha256, sha384, sha512" + printf "create md5 (m), sha256 (s), sha512 (S) (or type one of the above checksums) [default=m]: " + read -r chsum_resp + for chks in md5 sha1 sha224 sha256 sha384 sha512 + do + if [ "$chsum_resp" = "$chks" ]; then + chsum=$chsum_resp + return + fi + done + if [ "$chsum_resp" = "s" ]; then + chsum=sha256 + elif [ "$chsum_resp" = "S" ]; then + chsum=sha512 + fi +} + +if [ -s "$selection" ]; then + printf "work with selection (s) or current file (f) [default=f]: " + read -r resp +fi + +if [ "$resp" = "s" ]; then + checksum_type + sed 's|'"$PWD/"'||g' < "$selection" | xargs -0 -I{} ${chsum}sum {} > "checksum_$(date '+%Y%m%d%H%M').$chsum" +elif [ -n "$1" ]; then + if [ -f "$1" ]; then + for chks in md5 sha1 sha224 sha256 sha384 sha512 + do + if echo "$1" | grep -q \.${chks}$; then + ${chks}sum -c < "$1" + read -r _ + return + fi + done + checksum_type + file=$(basename "$1").$chsum + ${chsum}sum "$1" > "$file" + elif [ -d "$1" ]; then + checksum_type + file=$(basename "$1").$chsum + find "$1" -type f -exec ${chsum}sum "{}" + > "$file" + fi +fi diff --git a/nnn/.config/nnn/plugins/diffs b/nnn/.config/nnn/plugins/diffs new file mode 100755 index 0000000..294c5e7 --- /dev/null +++ b/nnn/.config/nnn/plugins/diffs @@ -0,0 +1,56 @@ +#!/usr/bin/env sh + +# Description: Show diff of 2 directories or multiple files in vimdiff +# +# Note: 1. vim may show the warning: 'Vim: Warning: Input is not from a terminal' +# press 'Enter' to ignore and proceed. +# 2. if only one file is in selection, the hovered file is considered as the +# second file to diff with +# +# Shell: POSIX compliant +# Authors: Arun Prakash Jana, ath3 + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +if which nvim >/dev/null 2>&1; then + diffcmd="nvim -d" +else + diffcmd="vimdiff +0" +fi + +dirdiff() { + dir1=$(mktemp "${TMPDIR:-/tmp}"/nnn-"$(basename "$1")".XXXXXXXX) + dir2=$(mktemp "${TMPDIR:-/tmp}"/nnn-"$(basename "$2")".XXXXXXXX) + ls -A1 "$1" > "$dir1" + ls -A1 "$2" > "$dir2" + $diffcmd "$dir1" "$dir2" + rm "$dir1" "$dir2" +} + +if [ -s "$selection" ]; then + arr=$(tr '\0' '\n' < "$selection") + if [ "$(echo "$arr" | wc -l)" -gt 1 ]; then + f1="$(echo "$arr" | sed -n '1p')" + f2="$(echo "$arr" | sed -n '2p')" + if [ -d "$f1" ] && [ -d "$f2" ]; then + dirdiff "$f1" "$f2" + else + # If xargs supports the -o option, use it to get rid of: + # Vim: Warning: Input is not from a terminal + # xargs -0 -o vimdiff < $selection + + eval xargs -0 "$diffcmd" < "$selection" + fi + elif ! [ -z "$1" ]; then + f1="$(echo "$arr" | sed -n '1p')" + if [ -d "$f1" ] && [ -d "$1" ]; then + dirdiff "$f1" "$1" + elif [ -f "$f1" ] && [ -f "$1" ]; then + $diffcmd "$f1" "$1" + else + echo "cannot compare file with directory" + fi + else + echo "needs at least 2 files or directories selected for comparison" + fi +fi diff --git a/nnn/.config/nnn/plugins/dragdrop b/nnn/.config/nnn/plugins/dragdrop index 8623f24..a69ed84 100755 --- a/nnn/.config/nnn/plugins/dragdrop +++ b/nnn/.config/nnn/plugins/dragdrop @@ -3,13 +3,13 @@ # Description: Open a Drag and drop window, to drop files onto other programs. # Also provides drag and drop window for files. # -# Dependencies: dragon - https://github.com/mwh/dragon +# Dependencies: https://github.com/mwh/dragon # # Notes: -# 1. Files that are dropped will be added to nnn's selection -# Some web-based files will be downloaded to current dir -# with curl and it may overwrite some existing files -# 2. The user has to mm to clear nnn's selection first +# - Files that are dropped will be added to nnn's selection +# Some webbased files will be downloaded to current directory with curl +# and it may overwrite some existing files +# - The user has to mm to clear nnn's selection first # # Shell: POSIX compliant # Author: 0xACE @@ -17,7 +17,7 @@ selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} resp=f all= -if type dragon-drag-and-drop >/dev/null 2>&1; then +if which dragon-drag-and-drop >/dev/null 2>&1; then dnd="dragon-drag-and-drop" else dnd="dragon" @@ -72,3 +72,4 @@ else "$dnd" "$1" & fi fi + diff --git a/nnn/.config/nnn/plugins/dups b/nnn/.config/nnn/plugins/dups new file mode 100755 index 0000000..98e8f72 --- /dev/null +++ b/nnn/.config/nnn/plugins/dups @@ -0,0 +1,70 @@ +#!/usr/bin/env sh + +# Description: List non-empty duplicate files in the current directory (based on size followed by MD5) +# +# Source: https://www.commandlinefu.com/commands/view/3555/find-duplicate-files-based-on-size-first-then-md5-hash +# +# Dependencies: find md5sum sort uniq xargs gsed +# +# Note: bash compatible required for mktemp +# +# Shell: bash +# Authors: syssyphus, KlzXS + +# If the size of a file has more that $size_digits digits the file will be misplaced +# 12 digits fit files up to 931GiB + +EDITOR="${EDITOR:-vi}" +TMPDIR="${TMPDIR:-/tmp}" + +size_digits=12 +tmpfile=$(mktemp "$TMPDIR/.nnnXXXXXX") + +printf "\ +## This is an overview of all duplicate files found. +## Comment out the files you wish to remove. You will be given an option to cancel. +## Lines with double comments (##) are ignored. +## You will have the option to remove the files with force or interactively.\n +" > "$tmpfile" + +# shellcheck disable=SC2016 +find . -size +0 -type f -printf "%${size_digits}s %p\n" | sort -rn | uniq -w"${size_digits}" -D | sed -E ' +s/^ {,12}([0-9]{,12}) (.*)$/printf "%s %s\\n" "$(md5sum "\2")" "d\1"/ +' | tr '\n' '\0' | xargs -0 -n1 sh -c | sort | { uniq -w32 --all-repeated=separate; echo; } | sed -nE ' +h +s/^(.{32}).* d([0-9]*)$/## md5sum: \1 size: \2 bytes/p +g + +:loop +N +/.*\n$/!b loop +p' | sed -E 's/^.{32} (.*) d[0-9]*$/\1/' >> "$tmpfile" + +"$EDITOR" "$tmpfile" + +printf "Remove commented files? (yes/no) [default=n]: " +read -r commented + +if [ "$commented" = "y" ]; then + sedcmd="/^(##|[^#]).*/d; /^$/d; s/^# *(.*)$/\1/" +else + printf "Press any key to exit" + read -r _ + exit +fi + +printf "Remove with force or interactive? (f/i) [default=i]: " +read -r force + +if [ "$force" = "f" ]; then + #shellcheck disable=SC2016 + sed -E "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -f "$0" "$@" </dev/tty' +else + #shellcheck disable=SC2016 + sed -E "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -i "$0" "$@" </dev/tty' +fi + +rm "$tmpfile" + +printf "Press any key to exit" +read -r _ diff --git a/nnn/.config/nnn/plugins/finder b/nnn/.config/nnn/plugins/finder new file mode 100755 index 0000000..649b711 --- /dev/null +++ b/nnn/.config/nnn/plugins/finder @@ -0,0 +1,28 @@ +#!/usr/bin/env sh + +# Description: Run custom search and list results in smart context +# +# Note: To enable multi select in fzf, export the following: +# - export FZF_DEFAULT_OPTS='--bind ctrl-a:select-all,ctrl-d:deselect-all,ctrl-t:toggle' +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +. "$(dirname "$0")"/.nnn-plugin-helper + +printf "Examples:\n" +printf " find . -name \"pattern*\" -print0\n" +printf " fd pattern -0\n" +printf " find -maxdepth 1 -size +100M -print0\n" +printf " fd -d 2 -S +100M -0\n" +printf " grep -rlZ pattern\n" +printf " rg -l0 pattern\n" +printf " fzf -m | tr %s %s\n\n" "'\n'" "'\0'" + +printf "cmd: " +read -r cmd + +if ! [ -z "$cmd" ]; then + printf "%s" "+l" > "$NNN_PIPE" + eval "$cmd" > "$NNN_PIPE" +fi diff --git a/nnn/.config/nnn/plugins/fzcd b/nnn/.config/nnn/plugins/fzcd new file mode 100755 index 0000000..8cc21ab --- /dev/null +++ b/nnn/.config/nnn/plugins/fzcd @@ -0,0 +1,37 @@ +#!/usr/bin/env sh + +# Description: Run fzf and go to the directory of the file selected +# +# Shell: POSIX compliant +# Author: Anna Arad + +. "$(dirname "$0")"/.nnn-plugin-helper + +if [ "$(cmd_exists fzf)" -eq "0" ]; then + sel=$(fzf) +else + exit 1 +fi + +if [ -n "$sel" ]; then + if ! [ -d "$sel" ]; then + sel=$(dirname "$sel") + elif [ "$sel" = "." ]; then + exit 0 + fi + + # Check if selected path returned + # by fzf command is absolute + case $sel in + /*) nnn_cd "$sel" ;; + *) + # Remove "./" prefix if it exists + sel="${sel#./}" + + if [ "$PWD" = "/" ]; then + nnn_cd "/$sel" + else + nnn_cd "$PWD/$sel" + fi;; + esac +fi diff --git a/nnn/.config/nnn/plugins/fzhist b/nnn/.config/nnn/plugins/fzhist new file mode 100755 index 0000000..a3aac3a --- /dev/null +++ b/nnn/.config/nnn/plugins/fzhist @@ -0,0 +1,38 @@ +#!/usr/bin/env sh + +# Description: Fuzzy find a command from history, edit in $EDITOR and run as a command +# Currently supports only bash and fish history +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if which fzf >/dev/null 2>&1; then + fuzzy=fzf +else + exit 1 +fi + +shellname="$(basename "$SHELL")" + +if [ "$shellname" = "bash" ]; then + hist_file="$HOME/.bash_history" + entry="$("$fuzzy" < "$hist_file")" +elif [ "$shellname" = "fish" ]; then + hist_file="$HOME/.config/fish/fish_history" + entry="$(grep "\- cmd: " "$hist_file" | cut -c 8- | "$fuzzy")" +fi + +if ! [ -z "$entry" ]; then + tmpfile=$(mktemp) + echo "$entry" >> "$tmpfile" + $EDITOR "$tmpfile" + + if [ -s "$tmpfile" ]; then + $SHELL -c "$(cat "$tmpfile")" + fi + + rm "$tmpfile" + + printf "Press any key to exit" + read -r _ +fi diff --git a/nnn/.config/nnn/plugins/fzopen b/nnn/.config/nnn/plugins/fzopen new file mode 100755 index 0000000..fa44e0f --- /dev/null +++ b/nnn/.config/nnn/plugins/fzopen @@ -0,0 +1,38 @@ +#!/usr/bin/env sh + +# Description: Fuzzy find a file in directory subtree +# Opens in $VISUAL or $EDITOR if text +# Opens other type of files with xdg-open +# +# Dependencies: fd/find, fzf/skim, xdg-open +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if which fzf >/dev/null 2>&1; then + cmd="$FZF_DEFAULT_COMMAND" + if which fd >/dev/null 2>&1; then + [ -z "$cmd" ] && cmd="fd -t f 2>/dev/null" + else + [ -z "$cmd" ] && cmd="find . -type f 2>/dev/null" + fi + entry="$(eval "$cmd" | fzf --delimiter / --nth=-1 --tiebreak=begin --info=hidden)" + # To show only the file name + # entry=$(find . -type f 2>/dev/null | fzf --delimiter / --with-nth=-1 --tiebreak=begin --info=hidden) +elif which sk >/dev/null 2>&1; then + entry=$(find . -type f 2>/dev/null | sk) +else + exit 1 +fi + +case "$(file -biL "$entry")" in + *text*) + "${VISUAL:-$EDITOR}" "$entry" ;; + *) + if uname | grep -q "Darwin"; then + open "$entry" >/dev/null 2>&1 + else + xdg-open "$entry" >/dev/null 2>&1 + fi + ;; +esac diff --git a/nnn/.config/nnn/plugins/fzz b/nnn/.config/nnn/plugins/fzz new file mode 100755 index 0000000..049cf9d --- /dev/null +++ b/nnn/.config/nnn/plugins/fzz @@ -0,0 +1,26 @@ +#!/usr/bin/env sh + +# Description: cd to any dir in the z database using an fzf pane +# +# Shell: POSIX compliant +# Author: Nick Waywood + +. "$(dirname "$0")"/.nnn-plugin-helper + +if which fzf >/dev/null 2>&1; then + fuzzy=fzf +else + exit 1 +fi + +datafile="${_Z_DATA:-$HOME/.z}" +if [ -f "$datafile" ]; then + # I read the data from z's file instead of calling the z command so that the data doesn't need to be processed twice + sel=$(awk -F "|" '{print $1}' "$datafile" | "$fuzzy" | awk '{$1=$1};1') + # NOTE: Uncomment this line and comment out the line above if you want to see the weightings of the dir's in the fzf pane + # sel=$(awk -F "|" '{printf "%s %s\n", $2, $1}' "$datafile" | "$fuzzy" | sed 's/^[0-9,.]* *//' | awk '{$1=$1};1') +else + exit 1 +fi + +printf "%s" "0c$sel" > "$NNN_PIPE" diff --git a/nnn/.config/nnn/plugins/getplugs b/nnn/.config/nnn/plugins/getplugs new file mode 100755 index 0000000..d875074 --- /dev/null +++ b/nnn/.config/nnn/plugins/getplugs @@ -0,0 +1,83 @@ +#!/usr/bin/env sh + +# Description: Update nnn plugins to installed nnn version +# +# Shell: POSIX compliant +# Authors: Arun Prakash Jana, KlzXS + +CONFIG_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/ +PLUGIN_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins + +# is_cmd_exists () { +# which "$1" > /dev/null 2>&1 +# echo $? +# } + +merge () { + if which nvim >/dev/null 2>&1; then + nvim -d "$1" "$2" + else + vimdiff +0 "$1" "$2" + fi +} + +prompt () { + printf "%s\n" "Plugin $1 already exists and is different." + printf "Keep (k), merge (m), overwrite (o) [default: k]? " + read -r operation + + if [ "$operation" = "m" ]; then + op="merge" + elif [ "$operation" = "o" ]; then + op="cp -vRf" + else + op="true" + fi +} + +# if [ "$(is_cmd_exists sudo)" -eq "0" ]; then +# sucmd=sudo +# elif [ "$(is_cmd_exists doas)" -eq "0" ]; then +# sucmd=doas +# else +# sucmd=: # noop +# fi + +if [ "$1" = "master" ] ; then + VER="master" + ARCHIVE_URL=https://github.com/jarun/nnn/archive/master.tar.gz +elif which nnn >/dev/null 2>&1; then + VER=$(nnn -V) + ARCHIVE_URL=https://github.com/jarun/nnn/releases/download/v"$VER"/nnn-v"$VER".tar.gz +else + echo "nnn is not installed" + exit 1 +fi + +# backup any earlier plugins +if [ -d "$PLUGIN_DIR" ]; then + tar -C "$CONFIG_DIR" -czf "$CONFIG_DIR""plugins-$(date '+%Y%m%d%H%M').tar.gz" plugins/ +fi + +mkdir -p "$PLUGIN_DIR" +cd "$CONFIG_DIR" || exit 1 +curl -Ls "$ARCHIVE_URL" -o nnn-"$VER".tar.gz +tar -zxf nnn-"$VER".tar.gz + +cd nnn-"$VER"/plugins || exit 1 + +# shellcheck disable=SC2044 +# We do not use obnoxious names for plugins +for f in $(find . -maxdepth 1 \( ! -iname "." ! -iname "*.md" \)); do + if [ -f ../../plugins/"$f" ]; then + if [ "$(diff --brief "$f" ../../plugins/"$f")" ]; then + prompt "$f" + $op "$f" ../../plugins/ + fi + else + cp -vRf "$f" ../../plugins/ + fi +done +cd ../.. || exit 1 + +rm -rf nnn-"$VER"/ nnn-"$VER".tar.gz diff --git a/nnn/.config/nnn/plugins/gpgd b/nnn/.config/nnn/plugins/gpgd new file mode 100755 index 0000000..0dde588 --- /dev/null +++ b/nnn/.config/nnn/plugins/gpgd @@ -0,0 +1,22 @@ +#!/usr/bin/env sh + +# Description: Decrypts selected files using gpg. The contents of the decrypted file are stored in a file with extension .dec +# +# Note: If an appropriate private key cannot be found gpg silently prints a message in the background and no files are written. +# +# Shell: POSIX compliant +# Author: KlzXS + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +printf "(s)election/(c)urrent? [default=c] " +read -r resp + +if [ "$resp" = "s" ]; then + files=$(tr '\0' '\n' < "$selection") +else + files=$1 +fi + +printf "%s" "$files" | xargs -n1 -I{} gpg --decrypt --output "{}.dec" {} + diff --git a/nnn/.config/nnn/plugins/gpge b/nnn/.config/nnn/plugins/gpge new file mode 100755 index 0000000..e69e116 --- /dev/null +++ b/nnn/.config/nnn/plugins/gpge @@ -0,0 +1,38 @@ +#!/usr/bin/env sh + +# Description: Encrypts selected files using gpg. Can encrypt either asymmetrically (key) or symmetrically (passphrase). +# If asymmetric encryption is chosen a key can be chosen from the list of capable public keys using fzf. +# +# Note: symmetric encryption only works for a single (current) file as per gpg limitations +# +# Shell: POSIX compliant +# Author: KlzXS + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +printf "(s)ymmetric, (a)symmetric? [default=a] " +read -r symmetry + +if [ "$symmetry" = "s" ]; then + gpg --symmetric "$1" +else + printf "(s)election/(c)urrent? [default=c] " + read -r resp + + if [ "$resp" = "s" ]; then + files=$(tr '\0' '\n' < "$selection") + else + files=$1 + fi + + keyids=$(gpg --list-public-keys --with-colons | grep -E "pub:(.*:){10}.*[eE].*:" | awk -F ":" '{print $5}') + + #awk needs literal $10 + #shellcheck disable=SC2016 + keyuids=$(printf "%s" "$keyids" | xargs -n1 -I{} sh -c 'gpg --list-key --with-colons "{}" | grep "uid" | awk -F ":" '\''{printf "%s %s\n", "{}", $10}'\''') + + recipient=$(printf "%s" "$keyuids" | fzf | awk '{print $1}') + + printf "%s" "$files" | xargs -n1 gpg --encrypt --recipient "$recipient" +fi + diff --git a/nnn/.config/nnn/plugins/gutenread b/nnn/.config/nnn/plugins/gutenread new file mode 100755 index 0000000..eb845bf --- /dev/null +++ b/nnn/.config/nnn/plugins/gutenread @@ -0,0 +1,49 @@ +#!/usr/bin/env sh + +# Description: Browse Project Gutenberg catalogue by popularity, then download +# and read a book of your choice. +# +# Details: Set the variable EBOOK_ID to download in html format and read in w3m. +# Clear EBOOK_ID to browse available ebooks by popularity and set it to +# the ID once you find an interesting one. +# To dowload and read in epub format set READER to an epub reader like +# epr: https://github.com/wustho/epr +# +# More on EBOOK_ID: +# Wuthering Heights by Emily Brontë is at https://www.gutenberg.org/ebooks/768 +# So EBOOK_ID would be 768 +# +# Downloaded ebooks are at ${XDG_CACHE_HOME:-$HOME/.cache}/nnn/gutenbooks/ +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +EBOOK_ID="${EBOOK_ID}" +DIR="${XDG_CACHE_HOME:-$HOME/.cache}/nnn/gutenbooks/$EBOOK_ID" +BROWSE_LINK="http://www.gutenberg.org/ebooks/search/?sort_order=downloads" +BROWSER="${BROWSER:-w3m}" +READER="${READER}" + +if [ -n "$EBOOK_ID" ]; then + if [ ! -e "$DIR" ]; then + mkdir -p "$DIR" + cd "$DIR" || exit 1 + + if [ -z "$READER" ]; then + curl -L -O "https://www.gutenberg.org/files/$EBOOK_ID/$EBOOK_ID-h.zip" + unzip "$EBOOK_ID"-h.zip + else + curl -L -o "$EBOOK_ID".epub "http://www.gutenberg.org/ebooks/$EBOOK_ID.epub.noimages" + fi + fi + + if [ -d "$DIR" ]; then + if [ -z "$READER" ]; then + "$BROWSER" "$DIR/$EBOOK_ID-h/$EBOOK_ID-h.htm" + else + "$READER" "$DIR/$EBOOK_ID.epub" + fi + fi +else + "$BROWSER" "$BROWSE_LINK" +fi diff --git a/nnn/.config/nnn/plugins/hexview b/nnn/.config/nnn/plugins/hexview new file mode 100755 index 0000000..ce17a60 --- /dev/null +++ b/nnn/.config/nnn/plugins/hexview @@ -0,0 +1,15 @@ +#!/usr/bin/env sh + +# Description: View a file in hex +# Dependencies: hx (https://github.com/krpors/hx)/xxd and $PAGER +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if ! [ -z "$1" ]; then + if which hx >/dev/null 2>&1; then + hx "$1" + else + xxd "$1" | $PAGER + fi +fi diff --git a/nnn/.config/nnn/plugins/imgresize b/nnn/.config/nnn/plugins/imgresize new file mode 100755 index 0000000..d85d4b5 --- /dev/null +++ b/nnn/.config/nnn/plugins/imgresize @@ -0,0 +1,30 @@ +#!/usr/bin/env sh + +# Description: Resize images in a directory to screen resolution with imgp +# imgp homepage: https://github.com/jarun/imgp +# +# Notes: +# 1. Set res if you don't want to be prompted for desktop resolution every time +# 2. MINSIZE is set to 1MB by default, adjust it if you want +# 3. imgp options used: +# a - adaptive mode +# c - convert PNG to JPG +# k - skip images matching specified hres/vres +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +# set resolution (e.g. 1920x1080) +res="${RESOLUTION}" + +# set minimum image size (in bytes) to resize (default: 1MB) +MINSIZE="${MINSIZE:-1048576}" + +if [ -z "$res" ]; then + printf "desktop resolution (hxv): " + read -r res +fi + +if ! [ -z "$res" ] && ! [ -z "$MINSIZE" ]; then + imgp -ackx "$res" -s "$MINSIZE" +fi diff --git a/nnn/.config/nnn/plugins/imgthumb b/nnn/.config/nnn/plugins/imgthumb new file mode 100755 index 0000000..8882115 --- /dev/null +++ b/nnn/.config/nnn/plugins/imgthumb @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +# Description: View thumbnail of an image or a directory of images with lsix +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if ! [ -z "$1" ]; then + if [ -d "$1" ]; then + lsix "$1"/* + else + lsix "$1" + fi + + printf "Press any key to exit..." + read -r _ +fi diff --git a/nnn/.config/nnn/plugins/imgur b/nnn/.config/nnn/plugins/imgur new file mode 100755 index 0000000..e95360c --- /dev/null +++ b/nnn/.config/nnn/plugins/imgur @@ -0,0 +1,595 @@ +#!/usr/bin/env bash + +########################################################################## +# The MIT License +# +# Copyright (c) jomo +# +# Permission is hereby granted, free of charge, +# to any person obtaining a copy of this software and +# associated documentation files (the "Software"), to +# deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom +# the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice +# shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +########################################################################## + +# https://github.com/jomo/imgur-screenshot +# https://imgur.com/tools +# +# Slightly modified for `nnn` integration +# +# Shell: bash +# Description: Upload an image file to imgur + +if [ "${1}" = "--debug" ]; then + echo "########################################" + echo "Enabling debug mode" + echo "Please remove credentials before pasting" + echo "########################################" + echo "" + uname -a + for arg in ${0} "${@}"; do + echo -n "'${arg}' " + done + echo -e "\n" + shift + set -x +fi + +current_version="v1.7.4" + +function is_mac() { + uname | grep -q "Darwin" +} + +### IMGUR-SCREENSHOT DEFAULT CONFIG #### + +# You can override the config in ~/.config/imgur-screenshot/settings.conf + +imgur_anon_id="ea6c0ef2987808e" +imgur_icon_path="${HOME}/Pictures/imgur.png" + +imgur_acct_key="" +imgur_secret="" +login="false" +album_title="" +album_id="" +credentials_file="${HOME}/.config/imgur-screenshot/credentials.conf" + +file_name_format="imgur-%Y_%m_%d-%H:%M:%S.png" # when using scrot, must end with .png! +file_dir="${HOME}/Pictures" + +upload_connect_timeout="5" +upload_timeout="120" +upload_retries="1" + +# shellcheck disable=SC2034 +if is_mac; then + screenshot_select_command="screencapture -i %img" + screenshot_window_command="screencapture -iWa %img" + screenshot_full_command="screencapture %img" + open_command="open %url" +else + screenshot_select_command="scrot -s %img" + screenshot_window_command="scrot %img" + screenshot_full_command="scrot %img" + open_command="xdg-open %url" +fi +open="true" + +mode="select" +edit_command="gimp %img" +edit="false" +exit_on_album_creation_fail="true" + +log_file="${HOME}/.imgur-screenshot.log" + +auto_delete="" +copy_url="true" +keep_file="true" +check_update="true" + +# NOTICE: if you make changes here, also edit the docs at +# https://github.com/jomo/imgur-screenshot/wiki/Config + +# You can override the config in ~/.config/imgur-screenshot/settings.conf + +############## END CONFIG ############## + +settings_path="${HOME}/.config/imgur-screenshot/settings.conf" +if [ -f "${settings_path}" ]; then + source "${settings_path}" +fi + +# dependency check +if [ "${1}" = "--check" ]; then + (which grep &>/dev/null && echo "OK: found grep") || echo "ERROR: grep not found" + if is_mac; then + if which growlnotify &>/dev/null; then + echo "OK: found growlnotify" + elif which terminal-notifier &>/dev/null; then + echo "OK: found terminal-notifier" + else + echo "ERROR: growlnotify nor terminal-notifier found" + fi + (which screencapture &>/dev/null && echo "OK: found screencapture") || echo "ERROR: screencapture not found" + (which pbcopy &>/dev/null && echo "OK: found pbcopy") || echo "ERROR: pbcopy not found" + else + (which notify-send &>/dev/null && echo "OK: found notify-send") || echo "ERROR: notify-send (from libnotify-bin) not found" + (which scrot &>/dev/null && echo "OK: found scrot") || echo "ERROR: scrot not found" + (which xclip &>/dev/null && echo "OK: found xclip") || echo "ERROR: xclip not found" + fi + (which curl &>/dev/null && echo "OK: found curl") || echo "ERROR: curl not found" + exit 0 +fi + + +# notify <'ok'|'error'> <title> <text> +function notify() { + if is_mac; then + if which growlnotify &>/dev/null; then + growlnotify --icon "${imgur_icon_path}" --iconpath "${imgur_icon_path}" --title "${2}" --message "${3}" + else + terminal-notifier -appIcon "${imgur_icon_path}" -contentImage "${imgur_icon_path}" -title "imgur: ${2}" -message "${3}" + fi + else + if [ "${1}" = "error" ]; then + notify-send -a ImgurScreenshot -u critical -c "im.error" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}" + else + notify-send -a ImgurScreenshot -u low -c "transfer.complete" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}" + fi + fi +} + +function take_screenshot() { + echo "Please select area" + is_mac || sleep 0.1 # https://bbs.archlinux.org/viewtopic.php?pid=1246173#p1246173 + + cmd="screenshot_${mode}_command" + cmd=${!cmd//\%img/${1}} + + if ! shot_err="$(${cmd} &>/dev/null)"; then #takes a screenshot with selection + echo "Failed to take screenshot '${1}': '${shot_err}'. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}" + notify error "Something went wrong :(" "Information has been logged" + exit 1 + fi +} + +function check_for_update() { + # exit non-zero on HTTP error, output only the body (no stats) but output errors, follow redirects, output everything to stdout + remote_version="$(curl --compressed -fsSL --stderr - "https://api.github.com/repos/jomo/imgur-screenshot/releases" | grep -Em 1 --color 'tag_name":\s*".*"' | cut -d '"' -f 4)" + if ! [ -z "$remote_version" ]; then + if [ ! "${current_version}" = "${remote_version}" ] && [ ! -z "${current_version}" ] && [ ! -z "${remote_version}" ]; then + echo "Update found!" + echo "Version ${remote_version} is available (You have ${current_version})" + notify ok "Update found" "Version ${remote_version} is available (You have ${current_version}). https://github.com/jomo/imgur-screenshot" + echo "Check https://github.com/jomo/imgur-screenshot/releases/${remote_version} for more info." + elif [ -z "${current_version}" ] || [ -z "${remote_version}" ]; then + echo "Invalid empty version string" + echo "Current (local) version: '${current_version}'" + echo "Latest (remote) version: '${remote_version}'" + else + echo "Version ${current_version} is up to date." + fi + else + echo "Failed to check for latest version: ${remote_version}" + fi +} + +function check_oauth2_client_secrets() { + if [ -z "${imgur_acct_key}" ] || [ -z "${imgur_secret}" ]; then + echo "In order to upload to your account, register a new application at:" + echo "https://api.imgur.com/oauth2/addclient" + echo "Select 'OAuth 2 authorization without a callback URL'" + echo "Then, set the imgur_acct_key (Client ID) and imgur_secret in your config." + exit 1 + fi +} + +function load_access_token() { + token_expire_time=0 + # check for saved access_token and its expiration date + if [ -f "${credentials_file}" ]; then + source "${credentials_file}" + fi + current_time="$(date +%s)" + preemptive_refresh_time="$((10*60))" + expired="$((current_time > (token_expire_time - preemptive_refresh_time)))" + if [ ! -z "${refresh_token}" ]; then + # token already set + if [ "${expired}" -eq "0" ]; then + # token expired + refresh_access_token "${credentials_file}" + fi + else + acquire_access_token "${credentials_file}" + fi +} + +function acquire_access_token() { + check_oauth2_client_secrets + # prompt for a PIN + authorize_url="https://api.imgur.com/oauth2/authorize?client_id=${imgur_acct_key}&response_type=pin" + echo "Go to" + echo "${authorize_url}" + echo "and grant access to this application." + read -rp "Enter the PIN: " imgur_pin + + if [ -z "${imgur_pin}" ]; then + echo "PIN not entered, exiting" + exit 1 + fi + + # exchange the PIN for access token and refresh token + response="$(curl --compressed -fsSL --stderr - \ + -F "client_id=${imgur_acct_key}" \ + -F "client_secret=${imgur_secret}" \ + -F "grant_type=pin" \ + -F "pin=${imgur_pin}" \ + https://api.imgur.com/oauth2/token)" + save_access_token "${response}" "${1}" +} + +function refresh_access_token() { + check_oauth2_client_secrets + token_url="https://api.imgur.com/oauth2/token" + # exchange the refresh token for access_token and refresh_token + if ! response="$(curl --compressed -fsSL --stderr - \ + -F "client_id=${imgur_acct_key}" \ + -F "client_secret=${imgur_secret}" \ + -F "grant_type=refresh_token" \ + -F "refresh_token=${refresh_token}" \ + "${token_url}" + )"; then + # curl failed + handle_upload_error "${response}" "${token_url}" + exit 1 + fi + save_access_token "${response}" "${1}" +} + +function save_access_token() { + if ! grep -q "access_token" <<<"${1}"; then + # server did not send access_token + echo "Error: Something is wrong with your credentials:" + echo "${1}" + exit 1 + fi + + access_token="$(grep -Eo 'access_token":".*"' <<<"${1}" | cut -d '"' -f 3)" + refresh_token="$(grep -Eo 'refresh_token":".*"' <<<"${1}" | cut -d '"' -f 3)" + expires_in="$(grep -Eo 'expires_in":[0-9]*' <<<"${1}" | cut -d ':' -f 2)" + token_expire_time="$(( $(date +%s) + expires_in ))" + + # create dir if not exist + mkdir -p "$(dirname "${2}")" 2>/dev/null + touch "${2}" && chmod 600 "${2}" + cat <<EOF > "${2}" +access_token="${access_token}" +refresh_token="${refresh_token}" +token_expire_time="${token_expire_time}" +EOF +} + +function fetch_account_info() { + response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/account/me)" + if grep -Eq '"success":\s*true' <<<"${response}"; then + username="$(grep -Eo '"url":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + echo "Logged in as ${username}." + echo "https://${username}.imgur.com" + else + echo "Failed to fetch info: ${response}" + fi +} + +function delete_image() { + response="$(curl --compressed -X DELETE -fsSL --stderr - -H "Authorization: Client-ID ${1}" "https://api.imgur.com/3/image/${2}")" + if grep -Eq '"success":\s*true' <<<"${response}"; then + echo "Image successfully deleted (delete hash: ${2})." >> "${3}" + else + echo "The Image could not be deleted: ${response}." >> "${3}" + fi +} + +function upload_authenticated_image() { + echo "Uploading '${1}'..." + title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)" + if [ -n "${album_id}" ]; then + response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)" + else + response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)" + fi + + # JSON parser premium edition (not really) + if grep -Eq '"success":\s*true' <<<"${response}"; then + img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https! + del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + + if [ ! -z "${auto_delete}" ]; then + export -f delete_image + echo "Deleting image in ${auto_delete} seconds." + nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" & + fi + + handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}" + else # upload failed + err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + test -z "${err_msg}" && err_msg="${response}" + handle_upload_error "${err_msg}" "${1}" + fi +} + +function upload_anonymous_image() { + echo "Uploading '${1}'..." + title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)" + if [ -n "${album_id}" ]; then + response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" https://api.imgur.com/3/image)" + else + response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" https://api.imgur.com/3/image)" + fi + # JSON parser premium edition (not really) + if grep -Eq '"success":\s*true' <<<"${response}"; then + img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https! + del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + + if [ ! -z "${auto_delete}" ]; then + export -f delete_image + echo "Deleting image in ${auto_delete} seconds." + nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" & + fi + + handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}" + else # upload failed + err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + test -z "${err_msg}" && err_msg="${response}" + handle_upload_error "${err_msg}" "${1}" + fi +} + +function handle_upload_success() { + echo "" + echo "image link: ${1}" + echo "delete link: ${2}" + + if [ "${copy_url}" = "true" ] && [ -z "${album_title}" ]; then + if is_mac; then + echo -n "${1}" | pbcopy + else + echo -n "${1}" | xclip -selection clipboard + fi + echo "URL copied to clipboard" + fi + + # print to log file: image link, image location, delete link + echo -e "${1}\t${3}\t${2}" >> "${log_file}" + + notify ok "Upload done!" "${1}" + +# if [ ! -z "${open_command}" ] && [ "${open}" = "true" ]; then +# open_cmd=${open_command//\%url/${1}} +# open_cmd=${open_cmd//\%img/${2}} +# echo "Opening '${open_cmd}'" +# eval "${open_cmd}" +# fi +} + +function handle_upload_error() { + error="Upload failed: \"${1}\"" + echo "${error}" + echo -e "Error\t${2}\t${error}" >> "${log_file}" + notify error "Upload failed :(" "${1}" +} + +function handle_album_creation_success() { + echo "" + echo "Album link: ${1}" + echo "Delete hash: ${2}" + echo "" + + notify ok "Album created!" "${1}" + + if [ "${copy_url}" = "true" ]; then + if is_mac; then + echo -n "${1}" | pbcopy + else + echo -n "${1}" | xclip -selection clipboard + fi + echo "URL copied to clipboard" + fi + + # print to log file: album link, album title, delete hash + echo -e "${1}\t\"${3}\"\t${2}" >> "${log_file}" +} + +function handle_album_creation_error() { + error="Album creation failed: \"${1}\"" + echo -e "Error\t${2}\t${error}" >> "${log_file}" + notify error "Album creation failed :(" "${1}" + if [ ${exit_on_album_creation_fail} ]; then + exit 1 + fi +} + +while [ ${#} != 0 ]; do + case "${1}" in + -h | --help) + echo "usage: ${0} [--debug] [-c | --check | -v | -h | -u]" + echo " ${0} [--debug] [option]... [file]..." + echo "" + echo " --debug Enable debugging, must be first option" + echo " -h, --help Show this help, exit" + echo " -v, --version Show current version, exit" + echo " --check Check if all dependencies are installed, exit" + echo " -c, --connect Show connected imgur account, exit" + echo " -o, --open <true|false> Override 'open' config" + echo " -e, --edit <true|false> Override 'edit' config" + echo " -i, --edit-command <command> Override 'edit_command' config (include '%img'), sets --edit 'true'" + echo " -l, --login <true|false> Override 'login' config" + echo " -a, --album <album_title> Create new album and upload there" + echo " -A, --album-id <album_id> Override 'album_id' config" + echo " -k, --keep-file <true|false> Override 'keep_file' config" + echo " -d, --auto-delete <s> Automatically delete image after <s> seconds" + echo " -u, --update Check for updates, exit" + echo " file Upload file instead of taking a screenshot" + exit 0;; + -v | --version) + echo "${current_version}" + exit 0;; + -s | --select) + mode="select" + shift;; + -w | --window) + mode="window" + shift;; + -f | --full) + mode="full" + shift;; + -o | --open) + # shellcheck disable=SC2034 + open="${2}" + shift 2;; + -e | --edit) + edit="${2}" + shift 2;; + -i | --edit-command) + edit_command="${2}" + edit="true" + shift 2;; + -l | --login) + login="${2}" + shift 2;; + -c | --connect) + load_access_token + fetch_account_info + exit 0;; + -a | --album) + album_title="${2}" + shift 2;; + -A | --album-id) + album_id="${2}" + shift 2;; + -k | --keep-file) + keep_file="${2}" + shift 2;; + -d | --auto-delete) + auto_delete="${2}" + shift 2;; + -u | --update) + check_for_update + exit 0;; + *) + upload_files=("${@}") + break;; + esac +done + +if [ "${login}" = "true" ]; then + # load before changing directory + load_access_token +fi + + +if [ -n "${album_title}" ]; then + if [ "${login}" = "true" ]; then + response="$(curl -fsSL --stderr - \ + -F "title=${album_title}" \ + -H "Authorization: Bearer ${access_token}" \ + https://api.imgur.com/3/album)" + else + response="$(curl -fsSL --stderr - \ + -F "title=${album_title}" \ + -H "Authorization: Client-ID ${imgur_anon_id}" \ + https://api.imgur.com/3/album)" + fi + if grep -Eq '"success":\s*true' <<<"${response}"; then # Album creation successful + echo "Album '${album_title}' successfully created" + album_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + handle_album_creation_success "http://imgur.com/a/${album_id}" "${del_id}" "${album_title}" + + if [ "${login}" = "false" ]; then + album_id="${del_id}" + fi + else # Album creation failed + err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" + test -z "${err_msg}" && err_msg="${response}" + handle_album_creation_error "${err_msg}" "${album_title}" + fi +fi + +if [ -z "${upload_files[*]}" ]; then + upload_files[0]="" +fi + +for upload_file in "${upload_files[@]}"; do + + if [ -z "${upload_file}" ]; then + cd "${file_dir}" || exit 1 + + # new filename with date + img_file="$(date +"${file_name_format}")" + take_screenshot "${img_file}" + else + # upload file instead of screenshot + img_file="${upload_file}" + fi + + # get full path + #cd "$(dirname "$(realpath "${img_file}")")" + #img_file="$(realpath "${img_file}")" + + # check if file exists + if ! [ -f "${img_file}" ]; then + echo "file '${img_file}' doesn't exist !" + read -r _ + exit 1 + fi + + # open image in editor if configured + if [ "${edit}" = "true" ]; then + edit_cmd=${edit_command//\%img/${img_file}} + echo "Opening editor '${edit_cmd}'" + if ! (eval "${edit_cmd}"); then + echo "Error for image '${img_file}': command '${edit_cmd}' failed, not uploading. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}" + notify error "Something went wrong :(" "Information has been logged" + exit 1 + fi + fi + + if [ "${login}" = "true" ]; then + upload_authenticated_image "${img_file}" + else + upload_anonymous_image "${img_file}" + fi + + # delete file if configured + if [ "${keep_file}" = "false" ] && [ -z "${1}" ]; then + echo "Deleting temp file ${file_dir}/${img_file}" + rm -rf "${img_file}" + fi + + echo "" +done + + +if [ "${check_update}" = "true" ]; then + check_for_update +fi + +read -r _ diff --git a/nnn/.config/nnn/plugins/imgview b/nnn/.config/nnn/plugins/imgview new file mode 100755 index 0000000..daa0845 --- /dev/null +++ b/nnn/.config/nnn/plugins/imgview @@ -0,0 +1,70 @@ +#!/usr/bin/env sh + +# Description: Open images in hovered directory and thumbnails +# open hovered image in sxiv or viu and browse other images in the directory +# Dependencies: imv (https://github.com/eXeC64/imv) or, +# sxiv (https://github.com/muennich/sxiv) or, +# viu (https://github.com/atanunq/viu), less +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +abspath() { + case "$1" in + /*) printf "%s\n" "$1";; + *) printf "%s\n" "$PWD/$1";; + esac +} + +listimages() { + find -L "$(dirname "$target")" -maxdepth 1 -type f -iregex \ + '.*\(jpe?g\|bmp\|webp\|png\|gif\)$' -print0 | sort -z +} + +view_dir() { + target="$(abspath "$2")" + count="$(listimages | grep -a -m 1 -ZznF "$target" | cut -d: -f1)" + + if [ -n "$count" ]; then + if [ "$1" = 'sxiv' ]; then + listimages | xargs -0 "$1" -an "$count" -- + else + listimages | xargs -0 "$1" -n "$count" -- + fi + else + shift + "$1" -- "$@" # fallback + fi +} + +if [ -z "$1" ] || ! [ -s "$1" ]; then + printf "empty file" + read -r _ + exit 1 +fi + +if uname | grep -q "Darwin"; then + if [ -f "$1" ]; then + open "$1" >/dev/null 2>&1 & + fi +# `imvr` is often callable as `imv` on Linux distros +# You might need to change the reference below +elif which imvr >/dev/null 2>&1; then + if [ -f "$1" ]; then + view_dir imvr "$1" >/dev/null 2>&1 & + elif [ -d "$1" ] || [ -h "$1" ]; then + imvr "$1" >/dev/null 2>&1 & + fi +elif which sxiv >/dev/null 2>&1; then + if [ -f "$1" ]; then + view_dir sxiv "$1" >/dev/null 2>&1 & + elif [ -d "$1" ] || [ -h "$1" ]; then + sxiv -aqt "$1" >/dev/null 2>&1 & + fi +elif which viu >/dev/null 2>&1; then + viu -n "$1" | less -R +else + printf "Please install imv/sxiv/viu and check their callable names match the plugin source" + read -r _ + exit 2 +fi diff --git a/nnn/.config/nnn/plugins/ipinfo b/nnn/.config/nnn/plugins/ipinfo new file mode 100755 index 0000000..4ff6f41 --- /dev/null +++ b/nnn/.config/nnn/plugins/ipinfo @@ -0,0 +1,13 @@ +#!/usr/bin/env sh + +# Description: Shows the external IP address and whois information. Useful over VPNs. +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +IP=$(curl -s ifconfig.me) + +whois "$IP" +echo your external IP address is "$IP" + +read -r _ diff --git a/nnn/.config/nnn/plugins/kdeconnect b/nnn/.config/nnn/plugins/kdeconnect new file mode 100755 index 0000000..f1cfd95 --- /dev/null +++ b/nnn/.config/nnn/plugins/kdeconnect @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +# Description: Send the selected files to your Android device using kdeconnect-cli. You must have installed and configured kdeconnect both on the Android device and on the PC. +# +# Shell: POSIX compliant +# Author: juacq97 + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +id=$(kdeconnect-cli -a --id-only | awk '{print $1}') +if [ "$(find "$selection")" ]; then + kdeconnect-cli -d "$id" --share "$(cat "$selection")" +# If you want a system notification, uncomment the next 3 lines. +# notify-send -a "Kdeconnect" "Sending $(cat "$selection")" +#else +# notify-send -a "Kdeconnect" "No file selected" +fi diff --git a/nnn/.config/nnn/plugins/launch b/nnn/.config/nnn/plugins/launch new file mode 100755 index 0000000..19855f9 --- /dev/null +++ b/nnn/.config/nnn/plugins/launch @@ -0,0 +1,42 @@ +#!/usr/bin/env sh + +# Description: Independent POSIX-compliant GUI application launcher. +# Fuzzy find executables in $PATH and launch an application. +# stdin, stdout, stderr are suppressed so CLI tools exit silently. +# +# To configure launch as an independent app launcher add a keybind +# to open launch in a terminal e.g., +# +# xfce4-terminal -e "${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/launch +# +# Dependencies: fzf +# +# Usage: launch [delay] +# delay is in seconds, if omitted launch waits for 1 sec +# +# Integration with nnn: launch is installed with other plugins, nnn picks it up. +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +# shellcheck disable=SC2086 + +IFS=':' + +get_selection() { + if which fzf >/dev/null 2>&1; then + { IFS=':'; ls -H $PATH; } | sort | fzf + else + exit 1 + fi +} + +if selection=$( get_selection ); then + setsid "$selection" 2>/dev/null 1>/dev/null & + + if ! [ -z "$1" ]; then + sleep "$1" + else + sleep 1 + fi +fi diff --git a/nnn/.config/nnn/plugins/mediainf b/nnn/.config/nnn/plugins/mediainf index 22ae824..15640b4 100755 --- a/nnn/.config/nnn/plugins/mediainf +++ b/nnn/.config/nnn/plugins/mediainf @@ -7,7 +7,7 @@ # Shell: POSIX compliant # Author: Arun Prakash Jana -if [ -n "$1" ] && [ -f "$1" ]; then +if ! [ -z "$1" ] && [ -f "$1" ]; then mediainfo "$1" | $PAGER # exiftool "$1" | $PAGER fi diff --git a/nnn/.config/nnn/plugins/mimelist b/nnn/.config/nnn/plugins/mimelist index d601cd8..743d305 100755 --- a/nnn/.config/nnn/plugins/mimelist +++ b/nnn/.config/nnn/plugins/mimelist @@ -1,15 +1,21 @@ - #!/usr/bin/env sh -# Description: Find and list files by mime type in smart context +# Description: Run fd/find in subtree and list files by mime type in smart context +# Dependencies: fd/find # # Shell: POSIX compliant # Author: Arun Prakash Jana . "$(dirname "$0")"/.nnn-plugin-helper +if [ "$(cmd_exists fd)" -eq "0" ]; then + fd=fd +else + fd=find +fi + printf "mime (e.g., video/audio/image): " read -r mime printf "%s" "+l" > "$NNN_PIPE" -find . | file -if- | grep "$mime" | awk -F: '{printf "%s\0", $1}' > "$NNN_PIPE" +$fd | file -if- | grep "$mime" | awk -F: '{printf "%s\0", $1}' > "$NNN_PIPE" diff --git a/nnn/.config/nnn/plugins/moclyrics b/nnn/.config/nnn/plugins/moclyrics new file mode 100755 index 0000000..04e899e --- /dev/null +++ b/nnn/.config/nnn/plugins/moclyrics @@ -0,0 +1,39 @@ +#!/usr/bin/env sh + +# Description: Fetches the lyrics of the track currently playing in MOC +# Dependencies: ddgr (https://github.com/jarun/ddgr) +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +# Check if MOC server is running +cmd=$(pgrep -x mocp 2>/dev/null) +ret=$cmd +if [ -z "$ret" ]; then + exit +fi + +# Grab the output +out="$(mocp -i)" + +# Check if anything is playing +state=$(echo "$out" | grep "State:" | cut -d' ' -f2) +if ! [ "$state" = 'PLAY' ]; then + exit +fi + +# Try by Artist and Song Title first +ARTIST="$(echo "$out" | grep 'Artist:' | cut -d':' -f2 | sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" +TITLE="$(echo "$out" | grep 'SongTitle:' | cut -d':' -f2 | sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" + +if ! [ -z "$ARTIST" ] && ! [ -z "$TITLE" ]; then + ddgr -w azlyrics.com --ducky "$ARTIST" "$TITLE" +else + # Try by file name + FILENAME="$(basename "$(echo "$out" | grep 'File:' | cut -d':' -f2)")" + FILENAME="$(echo "${FILENAME%%.*}" | tr -d -)" + + if ! [ -z "$FILENAME" ]; then + ddgr -w azlyrics.com --ducky "$FILENAME" + fi +fi diff --git a/nnn/.config/nnn/plugins/mocplay b/nnn/.config/nnn/plugins/mocplay new file mode 100755 index 0000000..39c7c9a --- /dev/null +++ b/nnn/.config/nnn/plugins/mocplay @@ -0,0 +1,84 @@ +#!/usr/bin/env sh + +# Description: Appends and optionally plays music in MOC +# +# Notes: +# - if selection is available, plays it, else plays the current file or directory +# - appends tracks and exits is MOC is running, else clears playlist and adds tracks +# - to let mocp shuffle tracks, set SHUFFLE=1 +# +# Shell: POSIX compliant +# Authors: Arun Prakash Jana, ath3 + +IFS="$(printf '\n\r')" +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} +cmd=$(pgrep -x mocp 2>/dev/null) +ret=$cmd + +SHUFFLE="${SHUFFLE:-0}" + +mocp_add () +{ + if [ "$SHUFFLE" = 1 ]; then + if [ "$resp" = "y" ]; then + arr=$(tr '\0' '\n' < "$selection") + elif [ -n "$1" ]; then + arr="$1" + fi + + for entry in $arr + do + if [ -d "$entry" ]; then + arr2=$arr2$(find "$entry" -type f \( ! -iname "*.m3u" ! -iname "*.pls" \)) + elif echo "$entry" | grep -qv '\.m3u$\|\.pls$' ; then + arr2=$(printf "%s\n%s" "$entry" "$arr2") + fi + done + + mocp -o shuffle + echo "$arr2" | xargs -d "\n" mocp -a + else + if [ "$resp" = "y" ]; then + xargs < "$selection" -0 mocp -a + else + mocp -a "$1" + fi + fi +} + +if [ ! -s "$selection" ] && [ -z "$1" ]; then + exit +fi + +if [ "$2" = "opener" ]; then + : +elif [ -s "$selection" ]; then + printf "Work with selection? Enter 'y' to confirm: " + read -r resp +fi + +if [ -z "$ret" ]; then + # mocp not running + mocp -S +else + # mocp running, check if it's playing + state=$(mocp -i | grep "State:" | cut -d' ' -f2) + + if [ "$state" = 'PLAY' ]; then + # add to playlist and exit + mocp_add "$1" + + # uncomment the line below to show mocp interface after appending + # mocp + + exit + fi +fi + +# clear selection and play +mocp -c +mocp_add "$1" "$resp" +mocp -p + +# uncomment the line below to show mocp interface after appending +# mocp diff --git a/nnn/.config/nnn/plugins/mp3conv b/nnn/.config/nnn/plugins/mp3conv new file mode 100755 index 0000000..b582cbc --- /dev/null +++ b/nnn/.config/nnn/plugins/mp3conv @@ -0,0 +1,41 @@ +#!/usr/bin/env sh + +# Description: Extract audio from multimedia files and convert to mp3 +# +# Dependency: ffmpeg compiled with libmp3lame audio codec support +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +outdir=_mp3files + +handle_multimedia() { + mime="${1}" + file="${2}" + + case "${mime}" in + audio/* | video/*) + ffmpeg -i "${file}" -vn -codec:a libmp3lame -q:a 2 "${outdir}/${file%.*}.mp3" + ;; + *) + ;; + esac +} + +printf "Process 'a'll in directory or 'c'urrent? " +read -r resp + +if [ "$resp" = "a" ]; then + if ! [ -e "${outdir}" ]; then + mkdir "${outdir}" + fi + + for f in *; do + if [ -f "${f}" ]; then + mimestr="$( file --dereference --brief --mime-type -- "${f}" )" + handle_multimedia "${mimestr}" "${f}" + fi + done +elif [ "$resp" = "c" ] && [ -f "$1" ]; then + ffmpeg -i "${1}" -vn -codec:a libmp3lame -q:a 2 "${1%.*}.mp3" +fi diff --git a/nnn/.config/nnn/plugins/nbak b/nnn/.config/nnn/plugins/nbak new file mode 100755 index 0000000..f9cb644 --- /dev/null +++ b/nnn/.config/nnn/plugins/nbak @@ -0,0 +1,75 @@ +#!/usr/bin/env sh + +# Description: Backup nnn configuration +# - config dir content +# - environment config +# - shell functions and aliases +# +# Shell: POSIX compliant +# Author: Léo Villeveygoux + +nnn_aliases="n nnn" + +outdir="nnn-$(whoami)@$(hostname)" + +outfile="${outdir}.tar.bz2" + +shellname="$(basename "$SHELL")" + +conffile="config.txt" + +configdir="${XDG_CONFIG_HOME:-$HOME/.config}/nnn" + +workdir="$PWD" + +tempdir="$(mktemp -d)" + +mkdir "$tempdir/$outdir" + +if [ ! -d "$tempdir" ]; then + echo "Can't create work directory." >&2 + exit 1 +fi + +cd "$tempdir/$outdir" || exit 1 + +# Backing up config dir content +cp -r "$configdir" . || exit 1 + +# Environment config +env | sed "s/'/'\\\\''/" |\ + awk '/^NNN_/{print "export '\''"$0"'\''"}' > "$conffile" + +# Shell functions/aliases +case "$shellname" in + bash) + for name in $nnn_aliases ; do + if [ "$(bash -ic "type -t $name")" = "function" ] ; then + bash -ic "type $name" | tail -n+2 >> "$conffile" + elif bash -ic "alias $name" >/dev/null 2>&1 ; then + bash -ic "alias $name" >> "$conffile" + fi + done + ;; + zsh) + for name in $nnn_aliases ; do + if zsh -ic "functions $name" ; then + zsh -ic "functions $name" >> "$conffile" + elif zsh -ic "alias $name" ; then + echo alias "$(zsh -ic "alias $name")" >> "$conffile" + fi + done + ;; + + *) + echo "Unknown shell, skipping alias/function checking." >&2 + ;; +esac + +cd .. || exit 1 + +printf "Saving as '%s' ... " "$workdir/$outfile" + +tar caf "$workdir/$outfile" "$outdir" && echo "Done" || echo "Failed" + +cd "$workdir" && rm -rf "$tempdir" diff --git a/nnn/.config/nnn/plugins/nmount b/nnn/.config/nnn/plugins/nmount new file mode 100755 index 0000000..566119e --- /dev/null +++ b/nnn/.config/nnn/plugins/nmount @@ -0,0 +1,53 @@ +#!/usr/bin/env sh + +# Description: Toggle mount status of a device using pmount +# If the device is not mounted, it will be mounted. +# If the device is mounted, it will be unmounted and powered down. +# +# Runs `lsblk` if 'l' is entered, exits on 'Return`. +# +# Note: +# - The script uses Linux-specific lsblk to list block devices. Alternatives: +# macOS: "diskutil list" +# BSD: "geom disk list" +# - The script uses udisksctl (from udisks2) to pwoer down devices. This is also Linux-specific. +# Users on non-Linux platforms can comment it and use an alterntive to power-down disks. +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +prompt="device name ['l' lists]: " + +lsblk + +printf "\nEnsure you aren't still in the mounted device.\n" +printf "%s" "$prompt" +read -r dev + +while ! [ -z "$dev" ] +do + if [ "$dev" = "l" ]; then + lsblk + elif [ "$dev" = "q" ]; then + exit + else + if grep -qs "$dev " /proc/mounts; then + sync + if pumount "$dev" + then + echo "$dev" unmounted. + if udisksctl power-off -b /dev/"$dev" + then + echo "$dev" ejected. + fi + fi + else + pmount "$dev" + echo "$dev" mounted to "$(lsblk -n /dev/"$dev" | rev | cut -d' ' -f1 | rev)". + fi + fi + + echo + printf "%s" "$prompt" + read -r dev +done diff --git a/nnn/.config/nnn/plugins/nuke b/nnn/.config/nnn/plugins/nuke new file mode 100755 index 0000000..b783633 --- /dev/null +++ b/nnn/.config/nnn/plugins/nuke @@ -0,0 +1,516 @@ +#!/usr/bin/env sh + +# ############################################################################# +# Description: Sample script to play files in apps by file type or mime +# +# Shell: POSIX compliant +# Usage: nuke filepath +# +# Integration with nnn: +# 1. Export the required config: +# export NNN_OPENER=/absolute/path/to/nuke +# # Otherwise, if nuke is in $PATH +# # export NNN_OPENER=nuke +# 2. Run nnn with the program option to indicate a CLI opener +# nnn -c +# # The -c program option overrides option -e +# 3. nuke 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. +# +# Guards against accidentally opening mime types like executables, shared libs etc. +# +# 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' or '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 enabled ones): +# most archives: list with atool, bsdtar +# rar: list with unrar +# 7-zip: list with 7z +# pdf: zathura (GUI), pdftotext, mutool, exiftool +# audio: mocplay (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool +# avi|mkv|mp4: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool +# log: vi +# torrent: rtorrent, transmission-show +# odt|ods|odp|sxw: odt2txt +# md: glow (https://github.com/charmbracelet/glow), lowdown (https://kristaps.bsd.lv/lowdown) +# htm|html|xhtml: w3m, lynx, elinks +# json: jq, python (json.tool module) +# Multimedia by mime: +# image/*: imv/sxiv (GUI), viu (https://github.com/atanunq/viu), img2txt, exiftool +# video/*: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool +# audio/*: mocplay (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool +# application/pdf: zathura (GUI), pdftotext, mutool, exiftool +# Other mimes: +# text/troff: man -l +# text/* | */xml: vi +# image/vnd.djvu): djvutxt, exiftool +# +# ToDo: +# 1. Adapt, test and enable all mimes +# 2. Clean-up the unnecessary exit codes +# ############################################################################# + +# set to 1 to enable GUI apps +GUI="${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") +EDITOR="${EDITOR:-vi}" +PAGER="${PAGER:-less -R}" +ext="${FNAME##*.}" +if [ -n "$ext" ]; then + ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" +fi + +is_mac() { + uname | grep -q "Darwin" +} + +handle_pdf() { + if [ "$GUI" -ne 0 ] && is_mac; then + open "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif [ "$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}" - | eval "$PAGER" + exit 0 + elif which mutool >/dev/null 2>&1; then + mutool draw -F txt -i -- "${FPATH}" 1-10 | eval "$PAGER" + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}" | eval "$PAGER" + exit 0 + fi +} + +handle_audio() { + if which mocp >/dev/null 2>&1 && which mocplay >/dev/null 2>&1; then + mocplay "${FPATH}" "opener" >/dev/null 2>&1 + exit 0 + elif which mpv >/dev/null 2>&1; then + mpv "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which media_client >/dev/null 2>&1; then + media_client play "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which mediainfo >/dev/null 2>&1; then + mediainfo "${FPATH}" | eval "$PAGER" + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}"| eval "$PAGER" + exit 0 + fi +} + +handle_video() { + if [ "$GUI" -ne 0 ] && is_mac; then + open "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif [ "$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" | eval "$PAGER" + exit 0 + elif which mediainfo >/dev/null 2>&1; then + mediainfo "${FPATH}" | eval "$PAGER" + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}"| eval "$PAGER" + exit 0 + 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}" | eval "$PAGER" + exit 0 + elif which bsdtar >/dev/null 2>&1; then + bsdtar --list --file "${FPATH}" | eval "$PAGER" + 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}" | eval "$PAGER" + fi + exit 1;; + 7z) + if which 7z >/dev/null 2>&1; then + ## Avoid password prompt by providing empty password + 7z l -p -- "${FPATH}" | eval "$PAGER" + exit 0 + fi + exit 1;; + + ## PDF + pdf) + handle_pdf + exit 1;; + + ## Audio + aac|flac|m4a|mid|midi|mpa|mp2|mp3|ogg|wav|wma) + handle_audio + exit 1;; + + ## Video + avi|mkv|mp4) + handle_video + exit 1;; + + ## Log files + log) + "$EDITOR" "${FPATH}" + exit 0;; + + ## BitTorrent + torrent) + if which rtorrent >/dev/null 2>&1; then + rtorrent "${FPATH}" + exit 0 + elif 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}" | eval "$PAGER" + exit 0 + fi + exit 1;; + + ## Markdown + md) + if which glow >/dev/null 2>&1; then + glow -sdark "${FPATH}" | eval "$PAGER" + exit 0 + elif which lowdown >/dev/null 2>&1; then + lowdown -Tterm "${FPATH}" | eval "$PAGER" + exit 0 + fi + ;; + + ## HTML + htm|html|xhtml) + ## Preview as text conversion + if which w3m >/dev/null 2>&1; then + w3m -dump "${FPATH}" | eval "$PAGER" + exit 0 + elif which lynx >/dev/null 2>&1; then + lynx -dump -- "${FPATH}" | eval "$PAGER" + exit 0 + elif which elinks >/dev/null 2>&1; then + elinks -dump "${FPATH}" | eval "$PAGER" + exit 0 + fi + ;; + + ## JSON + json) + if which jq >/dev/null 2>&1; then + jq --color-output . "${FPATH}" | eval "$PAGER" + exit 0 + elif which python >/dev/null 2>&1; then + python -m json.tool -- "${FPATH}" | eval "$PAGER" + exit 0 + fi + ;; + esac +} + +abspath() { + case "$1" in + /*) printf "%s\n" "$1";; + *) printf "%s\n" "$PWD/$1";; + esac +} + +listimages() { + find -L "$(dirname "$target")" -maxdepth 1 -type f -iregex \ + '.*\(jpe?g\|bmp\|webp\|png\|gif\)$' -print0 | sort -z +} + +load_dir() { + target="$(abspath "$2")" + count="$(listimages | grep -a -m 1 -ZznF "$target" | cut -d: -f1)" + + if [ -n "$count" ]; then + listimages | xargs -0 "$1" -n "$count" -- + else + shift + "$1" -- "$@" # fallback + fi +} + +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 ] && is_mac; then + open "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif [ "$GUI" -ne 0 ] && which imvr >/dev/null 2>&1; then + load_dir imvr "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif [ "$GUI" -ne 0 ] && which sxiv >/dev/null 2>&1; then + load_dir sxiv "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif which viu >/dev/null 2>&1; then + viu -n "${FPATH}" | eval "$PAGER" + exit 0 + elif which img2txt >/dev/null 2>&1; then + img2txt --gamma=0.6 -- "${FPATH}" | eval "$PAGER" + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}" | eval "$PAGER" + 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 overridden + ## as above), but might fail for unsupported types. + exit 7;; + + ## PDF + application/pdf) + handle_pdf + exit 1;; + + ## Audio + audio/*) + handle_audio + exit 1;; + + ## Video + video/*) + handle_video + 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 + ## Manpages + text/troff) + man -l "${FPATH}" + exit 0;; + + ## Text + text/* | */xml) + "$EDITOR" "${FPATH}" + exit 0;; + ## 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}" | eval "$PAGER" + exit 0 + elif which exiftool >/dev/null 2>&1; then + exiftool "${FPATH}" | eval "$PAGER" + exit 0 + fi + exit 1;; + esac +} + +handle_fallback() { + if [ "$GUI" -ne 0 ] && which xdg-open >/dev/null 2>&1; then + xdg-open "${FPATH}" >/dev/null 2>&1 & + exit 0 + elif [ "$GUI" -ne 0 ] && which open >/dev/null 2>&1; then + open "${FPATH}" >/dev/null 2>&1 & + exit 0 + fi + + echo '----- File details -----' && file --dereference --brief -- "${FPATH}" + exit 1 +} + +handle_blocked() { + case "${MIMETYPE}" in + application/x-sharedlib) + exit 0;; + + application/x-shared-library-la) + exit 0;; + + application/x-executable) + exit 0;; + + application/x-shellscript) + exit 0;; + + application/octet-stream) + exit 0;; + esac +} + +MIMETYPE="$( file --dereference --brief --mime-type -- "${FPATH}" )" +handle_extension +handle_multimedia "${MIMETYPE}" +handle_mime "${MIMETYPE}" +handle_blocked "${MIMETYPE}" +handle_fallback + +exit 1 diff --git a/nnn/.config/nnn/plugins/oldbigfile b/nnn/.config/nnn/plugins/oldbigfile new file mode 100755 index 0000000..0a21527 --- /dev/null +++ b/nnn/.config/nnn/plugins/oldbigfile @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +# Description: List files bigger than input size by ascending access date. +# +# Dependencies: find sort +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +printf "Min file size (MB): " +read -r size + +find . -size +"$size"M -type f -printf '%A+ %s %p\n' | sort + +echo "Press any key to exit" +read -r _ diff --git a/nnn/.config/nnn/plugins/organize b/nnn/.config/nnn/plugins/organize new file mode 100755 index 0000000..3fb34c1 --- /dev/null +++ b/nnn/.config/nnn/plugins/organize @@ -0,0 +1,53 @@ +#!/usr/bin/env sh + +# Description: Organize files in directories by category +# +# Shell: POSIX compliant +# Author: th3lusive + +organize() { + case "$(file -biL "$1")" in + *video*) + [ ! -d "Videos" ] && mkdir "Videos" + mv "$1" "Videos/$1" + printf "Moved %s to Videos\n" "$1" ;; + + *audio*) [ ! -d "Audio" ] && mkdir "Audio" + mv "$1" "Audio/$1" + printf "Moved %s to Audio\n" "$1" ;; + + *image*) + [ ! -d "Images" ] && mkdir "Images" + mv "$1" "Images/$1" + printf "Moved %s to Images\n" "$1" ;; + + *pdf*|*document*|*epub*|*djvu*|*cb*) + [ ! -d "Documents" ] && mkdir "Documents" + mv "$1" "Documents/$1" + printf "Moved %s to Documents\n" "$1" ;; + + *text*) + [ ! -d "Plaintext" ] && mkdir "Plaintext" + mv "$1" "Plaintext/$1" + printf "Moved %s to Plaintext\n" "$1" ;; + + *tar*|*xz*|*compress*|*7z*|*rar*|*zip*) + [ ! -d "Archives" ] && mkdir "Archives" + mv "$1" "Archives/$1" + printf "Moved %s to Archives\n" "$1" ;; + + *binary*) + [ ! -d "Binaries" ] && mkdir "Binaries" + mv "$1" "Binaries/$1" + printf "Moved %s to Binaries\n" "$1" ;; + esac +} + +main() { + for file in * + do + [ -f "$file" ] && organize "$file" + done +} + +main "$@" diff --git a/nnn/.config/nnn/plugins/pdfread b/nnn/.config/nnn/plugins/pdfread new file mode 100755 index 0000000..4f47992 --- /dev/null +++ b/nnn/.config/nnn/plugins/pdfread @@ -0,0 +1,30 @@ +#!/usr/bin/env sh + +# Description: Read a text or PDF file in British English +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if ! [ -z "$1" ]; then + tmpf="$(basename "$1")" + tmpf="${TMPDIR:-/tmp}"/"${tmpf%.*}" + + if [ "$(head -c 4 "$1")" = "%PDF" ]; then + # Convert using pdftotext + pdftotext -nopgbrk -layout "$1" - | sed 's/\xe2\x80\x8b//g' > "$tmpf".txt + + pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$tmpf".txt)" + + rm "$tmpf".txt + else + pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$1")" + fi + + # to jump around and note the time + mpv "$tmpf".wav + + # flat read but better quality + # play -qV0 "$tmpf".wav treble 2 gain -l 2 + + rm "$tmpf".wav +fi diff --git a/nnn/.config/nnn/plugins/pdfview b/nnn/.config/nnn/plugins/pdfview new file mode 100755 index 0000000..2b953b5 --- /dev/null +++ b/nnn/.config/nnn/plugins/pdfview @@ -0,0 +1,24 @@ +#!/usr/bin/env sh + +# Description: View a PDF file in pager +# +# Notes: +# - $PAGER must be 'less -R' or 'most' +# - To use mutool, uncomment the relevant lines and comment the pdftotext line +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if ! [ -z "$1" ]; then + if [ "$(head -c 4 "$1")" = "%PDF" ]; then + # Convert using pdftotext + pdftotext -nopgbrk -layout "$1" - | sed 's/\xe2\x80\x8b//g' | $PAGER + + # Convert using mutool + # file=`basename "$1"` + # txt=/tmp/"$file".txt + # mutool convert -o "$txt" "$1" + # eval $PAGER $txt + # rm "$txt" + fi +fi diff --git a/nnn/.config/nnn/plugins/preview-tabbed b/nnn/.config/nnn/plugins/preview-tabbed new file mode 100755 index 0000000..98a4be8 --- /dev/null +++ b/nnn/.config/nnn/plugins/preview-tabbed @@ -0,0 +1,211 @@ +#!/usr/bin/env bash + +# Description: tabbed/xembed based file previewer +# +# Note: This plugin needs a "NNN_FIFO" to work. See man. +# +# Dependencies: +# - tabbed (https://tools.suckless.org/tabbed): xembed host +# - xterm (or urxvt or st) : xembed client for text-based preview +# - mpv (https://mpv.io): xembed client for video/audio +# - sxiv (https://github.com/muennich/sxiv): xembed client for images +# - zathura (https://pwmt.org/projects/zathura): xembed client for PDF documents +# - nnn's nuke plugin for text preview and fallback (should be in plugins directory) +# nuke is a fallback for 'mpv', 'sxiv', and 'zathura', but it has has its own +# dependencies, see the script itself +# - vim (or any editor/pager really) +# - file +# - mktemp +# - xdotool (optional, to keep main window focused) +# +# How to use: +# First, install the dependencies. Then you need to set a NNN_FIFO path +# and set a key for the plugin, then start `nnn`: +# +# $ NNN_FIFO=/tmp/nnn.fifo nnn +# +# Then in `nnn`, launch the `preview-tabbed` plugin. +# +# If you provide the same NNN_FIFO to all nnn instances, there will be a +# single common preview window. I you provide different FIFO path, they +# will be independent. +# +# How it works: +# We use `tabbed` [1] as a xembed [2] host, to have a single window +# owning each previewer window. So each previewer must be a xembed client. +# For text previewers, this is not an issue, as there are a lot of +# xembed-able terminal emulator (we default to `xterm`, but examples are +# provided for `urxvt` and `st`). For graphic preview this can be trickier, +# but a few popular viewers are xembed-able, we use: +# - `mpv`: multimedia player, for video/audio preview +# - `sxiv`: image viewer +# - `zathura`: PDF viewer +# - but we allways fallback to `nuke` plugin +# +# [1]: http://tools.suckless.org/tabbed/ +# [2]: https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html +# +# Shell: bash (job control is weakly specified in POSIX) +# Author: Léo Villeveygoux + + +XDOTOOL_TIMEOUT=2 +PAGER=${PAGER:-"vim -R"} +NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke" + + +if which xterm >/dev/null 2>&1 ; then + TERMINAL="xterm -into" +elif which urxvt >/dev/null 2>&1 ; then + TERMINAL="urxvt -embed" +elif which st >/dev/null 2>&1 ; then + TERMINAL="st -w" +else + echo "No xembed term found" >&2 +fi + + +term_nuke () { + # $1 -> $XID, $2 -> $FILE + $TERMINAL "$1" -e "$NUKE" "$2" & +} + +start_tabbed () { + FIFO="$(mktemp -u)" + mkfifo "$FIFO" + + tabbed > "$FIFO" & + + jobs # Get rid of the "Completed" entries + + TABBEDPID="$(jobs -p %%)" + + if [ -z "$TABBEDPID" ] ; then + echo "Can't start tabbed" + exit 1 + fi + + read -r XID < "$FIFO" + + rm "$FIFO" +} + +get_viewer_pid () { + VIEWERPID="$(jobs -p %%)" +} + +kill_viewer () { + if [ -n "$VIEWERPID" ] && jobs -p | grep "$VIEWERPID" ; then + kill "$VIEWERPID" + fi +} + +sigint_kill () { + kill_viewer + kill "$TABBEDPID" + exit 0 +} + +previewer_loop () { + unset -v NNN_FIFO + # mute from now + exec >/dev/null 2>&1 + + MAINWINDOW="$(xdotool getactivewindow)" + + start_tabbed + trap sigint_kill SIGINT + + xdotool windowactivate "$MAINWINDOW" + + # Bruteforce focus stealing prevention method, + # works well in floating window managers like XFCE + # but make interaction with the preview window harder + # (uncomment to use): + #xdotool behave "$XID" focus windowactivate "$MAINWINDOW" & + + while read -r FILE ; do + + jobs # Get rid of the "Completed" entries + + if ! jobs | grep tabbed ; then + break + fi + + if [ ! -e "$FILE" ] ; then + continue + fi + + kill_viewer + + MIME="$(file -b --mime-type "$FILE")" + + case "$MIME" in + video/*) + if which mpv >/dev/null 2>&1 ; then + mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" & + else + term_nuke "$XID" "$FILE" + fi + ;; + audio/*) + if which mpv >/dev/null 2>&1 ; then + mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" & + else + term_nuke "$XID" "$FILE" + fi + ;; + image/*) + if which sxiv >/dev/null 2>&1 ; then + sxiv -e "$XID" "$FILE" & + else + term_nuke "$XID" "$FILE" + fi + ;; + application/pdf) + if which zathura >/dev/null 2>&1 ; then + zathura -e "$XID" "$FILE" & + else + term_nuke "$XID" "$FILE" + fi + ;; + inode/directory) + $TERMINAL "$XID" -e nnn "$FILE" & + ;; + text/*) + if [ -x "$NUKE" ] ; then + term_nuke "$XID" "$FILE" + else + # shellcheck disable=SC2086 + $TERMINAL "$XID" -e $PAGER "$FILE" & + fi + ;; + *) + if [ -x "$NUKE" ] ; then + term_nuke "$XID" "$FILE" + else + $TERMINAL "$XID" -e sh -c "file '$FILE' | $PAGER -" & + fi + ;; + esac + get_viewer_pid + + # following lines are not needed with the bruteforce xdotool method + ACTIVE_XID="$(xdotool getactivewindow)" + if [ $((ACTIVE_XID == XID)) -ne 0 ] ; then + xdotool windowactivate "$MAINWINDOW" + else + timeout "$XDOTOOL_TIMEOUT" xdotool behave "$XID" focus windowactivate "$MAINWINDOW" & + fi + done + kill "$TABBEDPID" + kill_viewer +} + +if [ ! -r "$NNN_FIFO" ] ; then + echo "Can't read \$NNN_FIFO ('$NNN_FIFO')" + exit 1 +fi + +previewer_loop < "$NNN_FIFO" & +disown diff --git a/nnn/.config/nnn/plugins/preview-tui b/nnn/.config/nnn/plugins/preview-tui new file mode 100755 index 0000000..255c35d --- /dev/null +++ b/nnn/.config/nnn/plugins/preview-tui @@ -0,0 +1,231 @@ +#!/usr/bin/env sh + +# Description: Terminal based file previewer +# +# Note: This plugin needs a "NNN_FIFO" to work. See man. +# +# Dependencies: +# - Supports 3 independent methods to preview with: +# - tmux (>=3.0), or +# - kitty with allow_remote_control on, or +# - $TERMINAL set to a terminal (it's xterm by default). +# - less or $PAGER +# - tree or exa or ls +# - mediainfo or file +# - mktemp +# - unzip +# - tar +# - man +# - optional: bat for code syntax highlighting +# - optional: kitty terminal or catimg for images +# - optional: scope.sh file viewer from ranger. +# To use: +# 1. drop scope.sh executable in $PATH +# 2. set/export $USE_SCOPE as 1 +# - optional: pistol file viewer (https://github.com/doronbehar/pistol). +# To use: +# 1. install pistol +# 2. set/export $USE_PISTOL as 1 +# +# Usage: +# You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG, +# then start `nnn`: +# +# $ nnn -a +# +# or +# +# $ NNN_FIFO=/tmp/nnn.fifo nnn +# +# Then in `nnn`, launch the `preview-tui` plugin. +# +# If you provide the same NNN_FIFO to all nnn instances, there will be a +# single common preview window. If you provide different FIFO path (e.g. +# with -a), they will be independent. +# +# The previews will be shown in a tmux split. If that isn't possible, it +# will try to use a kitty terminal split. And as a final fallback, a +# different terminal window will be used ($TERMINAL). +# +# Tmux and kitty users can configure $SPLIT to either "h" or "v" to set a +# 'h'orizontal split or a 'v'ertical split (as in, the line that splits the +# windows will be horizontal or vertical). +# +# Kitty users need `allow_remote_control` set to `yes`. To customize the +# window split, `enabled_layouts` has to be set to `all` or `splits` (the +# former is the default value). This terminal is also able to show images +# without extra dependencies. +# +# Shell: POSIX compliant +# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero + +SPLIT="$SPLIT" # you can set a permanent split here +TERMINAL="$TERMINAL" # same goes for the terminal +USE_SCOPE="${USE_SCOPE:-0}" +USE_PISTOL="${USE_PISTOL:-0}" +PAGER="${PAGER:-less -R}" +[ "$PAGER" = "most" ] && PAGER="less -R" + +if [ -e "${TMUX%%,*}" ] && tmux -V | grep -q '[ -][3456789]\.'; then + TERMINAL=tmux +elif [ -n "$KITTY_WINDOW_ID" ] && kitty @ ls >/dev/null 2>&1; then + TERMINAL=kitty +else + TERMINAL="${TERMINAL:-xterm}" +fi + +if [ -z "$SPLIT" ] && [ $(($(tput lines) * 2)) -gt "$(tput cols)" ]; then + SPLIT='h' +elif [ "$SPLIT" != 'h' ]; then + SPLIT='v' +fi + +exists() { + which "$1" >/dev/null 2>&1 +} + +fifo_pager() { + cmd="$1" + shift + + # We use a FIFO to access $PAGER PID in jobs control + tmpfifopath="${TMPDIR:-/tmp}/nnn-preview-tui-fifo.$$" + mkfifo "$tmpfifopath" || return + + $PAGER < "$tmpfifopath" & + + ( + exec > "$tmpfifopath" + "$cmd" "$@" & + ) + + rm "$tmpfifopath" +} + +# Binary file: show file info inside the pager +print_bin_info() { + printf -- "-------- \033[1;31mBinary file\033[0m --------\n" + if exists mediainfo; then + mediainfo "$1" 2>/dev/null + else + file -b "$1" + fi +} + +preview_file () { + kill %- %+ 2>/dev/null && wait %- %+ 2>/dev/null + clear + + # Trying to use pistol if it's available. + if [ "$USE_PISTOL" -ne 0 ] && exists pistol; then + fifo_pager pistol "$1" + return + fi + + # Trying to use scope.sh if it's available. + if [ "$USE_SCOPE" -ne 0 ] && exists scope.sh; then + fifo_pager scope.sh "$1" "$cols" "$lines" "$(mktemp -d)" \ + "True" 2>/dev/null + return + fi + + # Detecting the exact type of the file: the encoding, mime type, and + # extension in lowercase. + encoding="$(file -Lb --mime-encoding -- "$1")" + mimetype="$(file -Lb --mime-type -- "$1")" + ext="${1##*.}" + if [ -n "$ext" ]; then + ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" + fi + lines=$(($(tput lines)-1)) + cols=$(tput cols) + + # Otherwise, falling back to the defaults. + if [ -d "$1" ]; then + cd "$1" || return + if exists tree; then + fifo_pager tree -L 3 -F + elif exists exa; then + fifo_pager exa -G --colour=always 2>/dev/null + else + fifo_pager ls --color=always + fi + elif [ "$encoding" = "binary" ]; then + if [ "${mimetype%%/*}" = "image" ] ; then + if [ "$TERMINAL" = "kitty" ]; then + # Kitty terminal users can use the native image preview method. + kitty +kitten icat --silent --transfer-mode=stream --stdin=no \ + "$1" & + elif exists catimg; then + catimg "$1" + elif exists viu; then + viu -t "$1" + else + fifo_pager print_bin_info "$1" + fi + elif [ "$mimetype" = "application/zip" ] ; then + fifo_pager unzip -l "$1" + elif [ "$ext" = "gz" ] || [ "$ext" = "bz2" ] ; then + fifo_pager tar -tvf "$1" + else + fifo_pager print_bin_info "$1" + fi + elif [ "$mimetype" = "text/troff" ] ; then + fifo_pager man -Pcat -l "$1" + else + if exists bat; then + fifo_pager bat --terminal-width="$cols" --paging=never --decorations=always --color=always \ + "$1" 2>/dev/null + else + $PAGER "$1" & + fi + fi +} + +if [ "$PREVIEW_MODE" ] ; then + if [ ! -r "$NNN_FIFO" ] ; then + echo "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')" >&2 + read -r + exit 1 + fi + + preview_file "$1" + + # use cat instead of 'exec <' to avoid issues with dash shell + # shellcheck disable=SC2002 + cat "$NNN_FIFO" |\ + while read -r selection ; do + preview_file "$selection" + done + + # Restoring the previous layout for kitty users. This will only work for + # kitty >= 0.18.0. + if [ "$TERMINAL" = "kitty" ]; then + kitty @ last-used-layout --no-response >/dev/null 2>&1 + fi + + exit 0 +fi + +if [ "$TERMINAL" = "tmux" ]; then + # tmux splits are inverted + if [ "$SPLIT" = "v" ]; then SPLIT="h"; else SPLIT="v"; fi + + tmux split-window -e "NNN_FIFO=$NNN_FIFO" -e "PREVIEW_MODE=1" -d"$SPLIT" "$0" "$1" +elif [ "$TERMINAL" = "kitty" ]; then + # Setting the layout for the new window. It will be restored after the + # script ends. + kitty @ goto-layout splits >/dev/null + + # Trying to use kitty's integrated window management as the split window. + # All environmental variables that will be used in the new window must + # be explicitly passed. + kitty @ launch --no-response --title "nnn preview" --keep-focus \ + --cwd "$PWD" --env "PATH=$PATH" --env "NNN_FIFO=$NNN_FIFO" \ + --env "PREVIEW_MODE=1" --env "PAGER=$PAGER" \ + --env "USE_SCOPE=$USE_SCOPE" --env "SPLIT=$SPLIT" \ + --env "USE_PISTOL=$USE_PISTOL" \ + --location "${SPLIT}split" "$0" "$1" >/dev/null +else + PREVIEW_MODE=1 $TERMINAL -e "$0" "$1" & +fi diff --git a/nnn/.config/nnn/plugins/pskill b/nnn/.config/nnn/plugins/pskill new file mode 100755 index 0000000..979492a --- /dev/null +++ b/nnn/.config/nnn/plugins/pskill @@ -0,0 +1,35 @@ +#!/usr/bin/env sh + +# Description: Fuzzy list and kill a (zombie) process by name +# +# Dependencies: fzf, ps +# +# Note: To kill a zombie process enter "zombie" +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +printf "Enter process name ['defunct' for zombies]: " +read -r psname + +# shellcheck disable=SC2009 +if ! [ -z "$psname" ]; then + if which sudo >/dev/null 2>&1; then + sucmd=sudo + elif which doas >/dev/null 2>&1; then + sucmd=doas + else + sucmd=: # noop + fi + + if which fzf >/dev/null 2>&1; then + fuzzy=fzf + else + exit 1 + fi + + cmd="$(ps -ax | grep -iw "$psname" | "$fuzzy" | sed -e 's/^[ \t]*//' | cut -d' ' -f1)" + if [ -n "$cmd" ]; then + $sucmd kill -9 "$cmd" + fi +fi diff --git a/nnn/.config/nnn/plugins/renamer b/nnn/.config/nnn/plugins/renamer new file mode 100755 index 0000000..68b6ef0 --- /dev/null +++ b/nnn/.config/nnn/plugins/renamer @@ -0,0 +1,41 @@ +#!/usr/bin/env sh + +# Description: Batch rename selection or current directory with qmv +# +# Notes: +# - Try to mimic current batch rename functionality but with correct +# handling of edge cases by qmv or vidir. +# Qmv opens with hidden files if no selection is used. Selected +# directories are shown. +# Vidir don't show directories nor hidden files. +# +# Shell: POSIX compliant +# Author: José Neder + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +if which qmv >/dev/null 2>&1; then + batchrenamesel="qmv -fdo -da" + batchrename="qmv -fdo -a" +elif which vidir >/dev/null 2>&1; then + batchrenamesel="vidir" + batchrename="vidir" +else + printf "there is not batchrename program installed." + exit +fi + +if [ -s "$selection" ]; then + printf "rename selection? " + read -r resp +fi + +if [ "$resp" = "y" ]; then + # -o flag is necessary for interactive editors + xargs -o -0 $batchrenamesel < "$selection" +elif [ ! "$(LC_ALL=C ls -a)" = ". +.." ]; then + # On older systems that don't have ls -A + $batchrename +fi + diff --git a/nnn/.config/nnn/plugins/ringtone b/nnn/.config/nnn/plugins/ringtone new file mode 100755 index 0000000..c18f255 --- /dev/null +++ b/nnn/.config/nnn/plugins/ringtone @@ -0,0 +1,36 @@ +#!/usr/bin/env sh + +# Description: Create an mp3 ringtone out of an audio file in any format +# Needs user to provide start and end where to cut the file +# Input file audio.ext results in audio_ringtone.mp3 +# +# Tip: To convert a complete media file, set start as 0 and +# the runtime of the file as end. +# +# Dependencies: date, ffmpeg +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if [ -n "$1" ]; then + printf "start (hh:mm:ss): " + read -r start + st=$(date -d "$start" +%s) || exit 1 + + printf "end (hh:mm:ss): " + read -r end + et=$(date -d "$end" +%s) || exit 1 + + if [ "$st" -ge "$et" ]; then + printf "error: start >= end " + read -r _ + exit 1 + fi + + interval=$(( et - st )) + + outfile=$(basename "$1") + outfile="${outfile%.*}"_ringtone.mp3 + + ffmpeg -i "$1" -ss "$start" -t "$interval" -vn -sn -acodec libmp3lame -q:a 2 "$outfile" +fi diff --git a/nnn/.config/nnn/plugins/rsynccp b/nnn/.config/nnn/plugins/rsynccp new file mode 100755 index 0000000..5cfa66f --- /dev/null +++ b/nnn/.config/nnn/plugins/rsynccp @@ -0,0 +1,26 @@ +#!/usr/bin/env sh + +# Description: Simple script to give copy-paste a progress percentage +# by utilizing rsync. +# +# LIMITATION: this won't work when pasting to MTP device. +# +# Dependencies: rsync +# +# Shell: POSIX compliant +# Author: Benawi Adha + +sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +# Choose one of these two schemes by commenting + +# more verbose +xargs -0 -a "$sel" -I % rsync -ah --progress % "$PWD" + +# less verbose +# xargs -0 -a "$sel" -I % rsync -ah --info=progress2 % "$PWD" + +# Clear selection +if [ -p "$NNN_PIPE" ]; then + printf "-" > "$NNN_PIPE" +fi diff --git a/nnn/.config/nnn/plugins/splitjoin b/nnn/.config/nnn/plugins/splitjoin new file mode 100755 index 0000000..545eadb --- /dev/null +++ b/nnn/.config/nnn/plugins/splitjoin @@ -0,0 +1,47 @@ +#!/usr/bin/env sh + +# Description: Splits the file passed as argument or joins selection +# +# Note: Adds numeric suffix to split files +# Adds '.out suffix to the first file to be joined and saves as output file for join +# +# Shell: POSIX compliant +# Authors: Arun Prakash Jana, ath3 + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} +resp=s + +if [ -s "$selection" ]; then + printf "press 's' (split current file) or 'j' (join selection): " + read -r resp +fi + +if [ "$resp" = "j" ]; then + if [ -s "$selection" ]; then + arr=$(tr '\0' '\n' < "$selection") + if [ "$(echo "$arr" | wc -l)" -lt 2 ]; then + echo "joining needs at least 2 files" + exit + fi + for entry in $arr + do + if [ -d "$entry" ]; then + echo "cant join directories" + exit + fi + done + + file="$(basename "$(echo "$arr" | sed -n '1p' | sed -e 's/[0-9][0-9]$//')")" + sort -z < "$selection" | xargs -0 -I{} cat {} > "${file}.out" + fi +elif [ "$resp" = "s" ]; then + if [ -n "$1" ] && [ -f "$1" ]; then + # a single file is passed + printf "split size in MB: " + read -r size + + if [ -n "$size" ]; then + split -d -b "$size"M "$1" "$1" + fi + fi +fi diff --git a/nnn/.config/nnn/plugins/suedit b/nnn/.config/nnn/plugins/suedit new file mode 100755 index 0000000..7598525 --- /dev/null +++ b/nnn/.config/nnn/plugins/suedit @@ -0,0 +1,21 @@ +#!/usr/bin/env sh + +# Description: Edit file as superuser +# +# Shell: POSIX compliant +# Author: Anna Arad + +EDITOR="${EDITOR:-vim}" + +is_cmd_exists () { + which "$1" > /dev/null 2>&1 + echo $? +} + +if [ "$(is_cmd_exists sudo)" -eq "0" ]; then + sudo "$EDITOR" "$1" +elif [ "$(is_cmd_exists sudoedit)" -eq "0" ]; then + sudoedit "$1" +elif [ "$(is_cmd_exists doas)" -eq "0" ]; then + doas "$EDITOR" "$1" +fi diff --git a/nnn/.config/nnn/plugins/toggleex b/nnn/.config/nnn/plugins/toggleex new file mode 100755 index 0000000..e42d65a --- /dev/null +++ b/nnn/.config/nnn/plugins/toggleex @@ -0,0 +1,21 @@ +#!/usr/bin/env sh + +# Description: Toggles executable mode for selection +# +# Dependencies: chmod +# +# Note: Works _only_ with selection (nnn can toggle the mode for the hovered file) +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +if [ -s "$selection" ]; then + xargs -0 -I {} sh -c 'if [ -x "{}" ] ; then chmod -x "{}" ; else chmod +x "{}" ; fi' < "$selection" + + # Clear selection + if [ -p "$NNN_PIPE" ]; then + printf "-" > "$NNN_PIPE" + fi +fi diff --git a/nnn/.config/nnn/plugins/treeview b/nnn/.config/nnn/plugins/treeview new file mode 100755 index 0000000..c8f8784 --- /dev/null +++ b/nnn/.config/nnn/plugins/treeview @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +# Description: Show tree output in $EDITOR +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +tree -ps | $EDITOR - diff --git a/nnn/.config/nnn/plugins/uidgid b/nnn/.config/nnn/plugins/uidgid new file mode 100755 index 0000000..d72f1e0 --- /dev/null +++ b/nnn/.config/nnn/plugins/uidgid @@ -0,0 +1,13 @@ +#!/usr/bin/env sh + +# Description: list uid and gid of files +# +# Note: To list UID and GID of all users in a pretty format, run: +# +# cut -d':' -f1,3,4,5 < /etc/passwd | column -ts ":" +# +# Shell: POSIX compliant +# Authors: Arun Prakash Jana, superDuperCyberTechno + +# shellcheck disable=SC2012 +ls -lah --group-directories-first | less diff --git a/nnn/.config/nnn/plugins/umounttree b/nnn/.config/nnn/plugins/umounttree new file mode 100755 index 0000000..011d74d --- /dev/null +++ b/nnn/.config/nnn/plugins/umounttree @@ -0,0 +1,52 @@ +#!/usr/bin/env sh + +# Description: Autodetects a nnn remote mountpoint (mounted with `c`) +# from any of its subfolders and allows unmounting it +# from the subdir without navigating to the mountppoint +# or entering the remote name. Also works when hovering +# the mountpoint directly like vanilla `u`. +# +# Dependencies: fusermount +# +# Shell: POSIX compliant +# Authors: Kabouik & 0xACE +# +# TODO: +# - Avoid lazy unmount by forcing nnn context to leave the subfolder before fusermount. +# Tried `printf "%s" "0c$m" > "$NNN_PIPE"` but it breaks the nnn interface, see #854. + +err=0 +m=$HOME/.config/nnn/mounts +if [ "$PWD" = "$m" ]; then + # Allow running the script on hovered directory if user is in ~/.config/nnn/mounts + d="$1" +else + d=$(dirname "$(readlink -f "$1")" | grep -oP "^$m\K.*" | cut -d"/" -f2) +fi + +# Test if user is within $m or a subdir, abort if not +if [ "$d" = "" ]; then + clear && printf "You are not in a remote folder mounted with nnn. Press return to continue. " && read -r _ +else + # Test if $m/$d is a mountpoint and try unmounting if it is + mountpoint -q -- "$m/$d" + if [ "$?" -eq "1" ]; then + clear && printf "Parent '%s' is not a mountpoint. Press return to continue. " "$d" && read -r _ + else + cd "$m" && fusermount -uq "$m/$d" || err=1 + if [ "$err" -eq "0" ]; then + rmdir "$m/$d" && clear && printf "Parent '%s' unmounted." "$d" + else + clear && printf "Failed to unmount. Try lazy unmount? [Yy/Nn] " && read -r + fi + fi +fi + +# If unmount fails, offer lazy unmount +if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then + err=0 + cd "$m" && fusermount -uqz "$m/$d" || err=1 + if [ "$err" -eq "0" ]; then + rmdir "$m/$d" && clear && printf "Parent '%s' unmounted with lazy unmount. " "$d" + fi +fi diff --git a/nnn/.config/nnn/plugins/upgrade b/nnn/.config/nnn/plugins/upgrade new file mode 100755 index 0000000..b2170f7 --- /dev/null +++ b/nnn/.config/nnn/plugins/upgrade @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +# Description: Check and update to latest version of nnn manually on Debian 9 Stretch +# +# Note: This script installs a package, should be issued with admin privilege +# +# Shell: POSIX-compliant +# Author: Arun Prakash Jana + +cur="$(nnn -v)" +new="$(curl -s "https://github.com/jarun/nnn/releases/latest" | grep -Eo "[0-9]+\.[0-9]+")" + +if [ "$cur" = "$new" ]; then + echo 'Already at latest version' + exit 0 +fi + +# get the package +curl -Ls -O "https://github.com/jarun/nnn/releases/download/v$new/nnn_$new-1_debian9.amd64.deb" + +# install it +sudo dpkg -i nnn_"$new"-1_debian9.amd64.deb + +# remove the file +rm -rf nnn_"$new"-1_debian9.amd64.deb diff --git a/nnn/.config/nnn/plugins/upload b/nnn/.config/nnn/plugins/upload new file mode 100755 index 0000000..196e8f7 --- /dev/null +++ b/nnn/.config/nnn/plugins/upload @@ -0,0 +1,28 @@ +#!/usr/bin/env sh + +# Description: Upload to Firefox Send if ffsend is found, else +# Paste contents of a text a file http://ix.io +# Upload a binary file to file.io +# Dependencies: ffsend (https://github.com/timvisee/ffsend), curl, jq, tr +# Note: Binary file set to expire after a week +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +if ! [ -z "$1" ] && [ -s "$1" ]; then + if which ffsend >/dev/null 2>&1; then + ffsend -iq u "$1" + elif [ "$(mimetype --output-format %m "$1" | awk -F '/' '{print $1}')" = "text" ]; then + curl -F "f:1=@$1" ix.io + else + # Upload the file, show the download link and wait till user presses any key + curl -s -F "file=@$1" https://file.io/?expires=1w | jq '.link' | tr -d '"' + + # To write download link to "$1".loc and exit + # curl -s -F "file=@$1" https://file.io/?expires=1w -o `basename "$1"`.loc + fi +else + printf "empty file!" +fi + +read -r _ diff --git a/nnn/.config/nnn/plugins/vidthumb b/nnn/.config/nnn/plugins/vidthumb new file mode 100755 index 0000000..248abd7 --- /dev/null +++ b/nnn/.config/nnn/plugins/vidthumb @@ -0,0 +1,27 @@ +#!/usr/bin/env sh + +# Description: Generate video thumbnails and view them +# +# Dependencies: +# ffmpegthumbnailer: https://github.com/dirkvdb/ffmpegthumbnailer +# lsix: https://github.com/hackerb9/lsix +# +# Shell: POSIX compliant +# Author: Arun Prakash Jana + +mkdir .nthumbs > /dev/null 2>&1 + +for file in *; do + if [ -f "$file" ]; then + ffmpegthumbnailer -i "$file" -o .nthumbs/"${file%%.*}".jpg 2> /dev/null + fi +done + +# render thumbnails in lsix +lsix .nthumbs/* + +# remove the thumbnails +rm -rf .nthumbs + +printf "Press any key to exit..." +read -r _ diff --git a/nnn/.config/nnn/plugins/wall b/nnn/.config/nnn/plugins/wall new file mode 100755 index 0000000..0fc6de7 --- /dev/null +++ b/nnn/.config/nnn/plugins/wall @@ -0,0 +1,30 @@ +#!/usr/bin/env sh + +# Description: Set the selected image as wallpaper using nitrogen or pywal. +# Usage: Hover on an image and run the script to set it as wallpaper. +# +# Shell: POSIX Compliant +# Author: juacq97 + +cmd_exists () { + which "$1" > /dev/null 2>&1 + echo $? +} + +if ! [ -z "$1" ]; then + if [ "$(mimetype --output-format %m "$1" | awk -F '/' '{print $1}')" = "image" ]; then + if [ "$(cmd_exists nitrogen)" -eq "0" ]; then + nitrogen --set-zoom-fill --save "$1" + elif [ "$(cmd_exists wal)" -eq "0" ]; then + wal -i "$1" + else + printf "nitrogen ir pywal missing" + read -r _ + fi + + # If you want a system notification, uncomment the next 3 lines. + # notify-send -a "nnn" "Wallpaper changed!" + # else + # notify-send -a "nnn" "No image selected" + fi +fi diff --git a/nnn/.config/nnn/plugins/x2sel b/nnn/.config/nnn/plugins/x2sel new file mode 100755 index 0000000..0a0fcd4 --- /dev/null +++ b/nnn/.config/nnn/plugins/x2sel @@ -0,0 +1,62 @@ +#!/usr/bin/env sh + +# Description: Copy system clipboard newline-separated file list to selection +# Dependencies: +# - tr +# - xclip/xsel (Linux) +# - pbpaste (macOS) +# - termux-clipboard-get (Termux) +# - powershell (WSL) +# - cygwim's /dev/clipboard (Cygwin) +# - wl-paste (Wayland) +# - clipboard (Haiku) +# +# Limitation: breaks if a filename has newline in it +# +# Shell: POSIX compliant +# Author: Léo Villeveygoux, after Arun Prakash Jana's .cbcp + +IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n + +selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} + +getclip () { + + if which xsel >/dev/null 2>&1; then + # Linux + xsel -bo + elif which xclip >/dev/null 2>&1; then + # Linux + xclip -sel clip -o + elif which pbpaste >/dev/null 2>&1; then + # macOS + pbpaste + elif which termux-clipboard-get >/dev/null 2>&1; then + # Termux + termux-clipboard-get + elif which powershell.exe >/dev/null 2>&1; then + # WSL + powershell.exe Get-Clipboard + elif [ -r /dev/clipboard ] ; then + # Cygwin + cat /dev/clipboard + elif which wl-paste >/dev/null 2>&1; then + # Wayland + wl-paste + elif which clipboard >/dev/null 2>&1; then + # Haiku + clipboard --print + fi + +} + +CLIPBOARD=$(getclip) + +# Check if clipboard actually contains a file list +for file in $CLIPBOARD ; do + if [ ! -e "$file" ] ; then + exit 1; + fi +done + +printf "%s" "$CLIPBOARD" | tr '\n' '\0' > "$selection" |
