mirror of
https://github.com/The-Repo-Club/DotFiles.git
synced 2024-11-25 00:38:20 -05:00
Added .local/bin
This commit is contained in:
parent
9d396c7796
commit
1e75df1729
27
.local/bin/fzf/fzf_menu_run
Executable file
27
.local/bin/fzf/fzf_menu_run
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# fzf_menu_run
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 10 March 2021, 12:34:47 PM [GMT+1]
|
||||||
|
#Modified:
|
||||||
|
# Thu 20 October 2022, 05:48:00 AM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# devour, fzf
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
programs=$(compgen -c | sort -u | tail -n +9)
|
||||||
|
cmd=$(echo -e "$programs" | fzf --prompt="Run Program: " --border=rounded --margin=1% --color=dark --height 100% --reverse --header=" PROGRAM MENU " --info=hidden --header-first)
|
||||||
|
exec gobble $cmd
|
78
.local/bin/fzf/fzf_music
Executable file
78
.local/bin/fzf/fzf_music
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# fzf_music
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 10 March 2021, 12:34:47 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Mon 21 November 2022, 07:44:23 AM [GMT]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# fzf music player
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# fzf
|
||||||
|
#
|
||||||
|
|
||||||
|
REPOMENU_MUSICPLAYER="ffplay -nodisp -loglevel quiet"
|
||||||
|
|
||||||
|
asksetting() {
|
||||||
|
playlist=$*
|
||||||
|
if [[ -z ${playlist} ]]; then
|
||||||
|
for Song in "$HOME"/Music/*; do
|
||||||
|
if [ -f "$Song" ]; then
|
||||||
|
Name=${Song##*/}
|
||||||
|
case $Name in
|
||||||
|
*.mp3 | *.flac | *.wav | .ogg)
|
||||||
|
options+=${Song##*/}$'\n'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
for Song in "$playlist"/*; do
|
||||||
|
if [ -f "$Song" ]; then
|
||||||
|
Name=${Song##*/}
|
||||||
|
case $Name in
|
||||||
|
*.mp3 | *.flac | *.wav | *.ogg)
|
||||||
|
options+=${Song##*/}$'\n'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo -e "${options::-1}
|
||||||
|
|
||||||
|
Close music player" | fzf --prompt="Song Search: " --border=rounded --margin=1% --color=dark --height 100% --reverse --header=" MUSIC MENU " --info=hidden --header-first
|
||||||
|
}
|
||||||
|
|
||||||
|
LOOPSETTING="true"
|
||||||
|
while [ -n "$LOOPSETTING" ]; do
|
||||||
|
CHOICE="$(asksetting "$1")"
|
||||||
|
[ -n "$CHOICE" ] || exit
|
||||||
|
unset LOOPSETTING
|
||||||
|
case $CHOICE in
|
||||||
|
*.mp3 | *.flac | *.wav | *.ogg)
|
||||||
|
playlist=$*
|
||||||
|
if [[ -z ${playlist} ]]; then
|
||||||
|
folder=$HOME/Music
|
||||||
|
else
|
||||||
|
folder=$playlist
|
||||||
|
fi
|
||||||
|
$REPOMENU_MUSICPLAYER "$folder/$CHOICE" &
|
||||||
|
;;
|
||||||
|
*Close*)
|
||||||
|
pkill "${REPOMENU_MUSICPLAYER%% *}" &
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Program terminated." && exit
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
115
.local/bin/fzf/fzf_pass
Executable file
115
.local/bin/fzf/fzf_pass
Executable file
@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# -*-coding:utf-8 -*-
|
||||||
|
# Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File :
|
||||||
|
# fzf_pass
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
# Created:
|
||||||
|
# Wed 10 March 2021, 12:34:47 PM [GMT]
|
||||||
|
# Modified:
|
||||||
|
# Thu 20 October 2022, 06:02:31 AM [GMT+1]
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# fzf
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
shopt -s nullglob globstar
|
||||||
|
|
||||||
|
typeit=0
|
||||||
|
if [[ $1 == "--type" ]]; then
|
||||||
|
typeit=1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
xdotool="xsel -b"
|
||||||
|
|
||||||
|
|
||||||
|
STARTDIR=${PASSWORD_STORE_DIR-~/.password-store}
|
||||||
|
BASEDIR=$STARTDIR
|
||||||
|
DONE=0
|
||||||
|
LEVEL=0
|
||||||
|
PREVSELECTION=""
|
||||||
|
SELECTION=""
|
||||||
|
URL_FIELD='url'
|
||||||
|
LOGIN_FIELD='login'
|
||||||
|
|
||||||
|
while [ "$DONE" -eq 0 ] ; do
|
||||||
|
password_files=( "$STARTDIR"/* )
|
||||||
|
password_files=( "${password_files[@]#"$STARTDIR"/}" )
|
||||||
|
password_files=( "${password_files[@]%.gpg}" )
|
||||||
|
|
||||||
|
if [ "$LEVEL" -ne 0 ] ; then
|
||||||
|
password_files=(".." "${password_files[@]}")
|
||||||
|
fi
|
||||||
|
entry=$(printf ' %s\n' "${password_files[@]}" | fzf --prompt="Password Search: " --border=rounded --margin=1% --color=dark --height 100% --reverse --header=" PASSWORD MENU " --info=hidden --header-first)
|
||||||
|
entry=$(echo $entry | sed "s/ //")
|
||||||
|
|
||||||
|
if [ -z "$entry" ] ; then
|
||||||
|
DONE=1
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$entry" != ".." ] ; then
|
||||||
|
PREVSELECTION=$SELECTION
|
||||||
|
SELECTION="$SELECTION/$entry"
|
||||||
|
|
||||||
|
# check if another dir
|
||||||
|
if [ -d "$STARTDIR/$entry" ] ; then
|
||||||
|
STARTDIR="$STARTDIR/$entry"
|
||||||
|
LEVEL=$((LEVEL+1))
|
||||||
|
else
|
||||||
|
# not a directory so it must be a real password entry
|
||||||
|
|
||||||
|
if [[ $typeit -eq 0 ]]; then
|
||||||
|
FIELDS=()
|
||||||
|
PASSWORD=$(pass show "$SELECTION" | head -1 | sed '/^$/d')
|
||||||
|
OTHER=$(pass show "$SELECTION" | awk -F: '(NR>1){ st = index($0,":");print $1 substr($0,st+1)}')
|
||||||
|
FIELDS+=" Password\n"
|
||||||
|
if [ ! -z "${OTHER}" ]; then
|
||||||
|
while read -r line; do
|
||||||
|
FIELD=$(echo -e "$line" | awk '{print $1}')
|
||||||
|
if [ "$FIELD" = "login" ]; then
|
||||||
|
FIELDS+=" ${FIELD^}\n"
|
||||||
|
elif [ "$FIELD" = "url" ]; then
|
||||||
|
FIELDS+="爵 ${FIELD^^}\n"
|
||||||
|
else
|
||||||
|
FIELDS+=" ${FIELD^}\n"
|
||||||
|
fi
|
||||||
|
done <<< "$OTHER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENTRY_FIELD=$(echo -e "$FIELDS" | sed '/^$/d' | fzf --prompt="Password Settings: " --border=rounded --margin=1% --color=dark --height 100% --reverse --header=" PASSWORD MENU " --info=hidden --header-first)
|
||||||
|
if [[ $ENTRY_FIELD = *'Password' ]]; then
|
||||||
|
PASSWORD=$(pass show "$SELECTION" | sed "s/^[ \t]*//" | head -1 | sed '/^$/d')
|
||||||
|
echo -n "$PASSWORD" | $xdotool
|
||||||
|
elif [[ $ENTRY_FIELD = *'URL' ]]; then
|
||||||
|
URL=$(pass show "$SELECTION" | grep "${URL_FIELD}" | awk '{sub(/:/,"")}{print $2}1' | sed "s/^[ \t]*//" | head -1 | sed '/^$/d')
|
||||||
|
echo -n "$URL" | $xdotool
|
||||||
|
elif [[ $ENTRY_FIELD = *'Login' ]]; then
|
||||||
|
LOGIN=$(pass show "$SELECTION" | grep "${LOGIN_FIELD}" | awk '{sub(/:/,"")}{print $2}1' | sed "s/^[ \t]*//" | head -1 | sed '/^$/d')
|
||||||
|
echo -n "$LOGIN" | $xdotool
|
||||||
|
else
|
||||||
|
CUSTOM_FIELD=$(echo -e "$ENTRY_FIELD" | awk 'NF>1 {sub("^[^A-Z]*","")} {print}')
|
||||||
|
CUSTOM_FIELD=$(pass show "$SELECTION" | grep "${CUSTOM_FIELD,,}" | awk '{sub(/:/,"")}{first = $1; $1=""; print $0;}1' | sed "s/^[ \t]*//" | head -1 | sed '/^$/d')
|
||||||
|
echo -n "$CUSTOM_FIELD" | $xdotool
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
xdotool - <<<"type --clearmodifiers -- $(pass show "$SELECTION" | head -n 1 | sed '/^$/d')"
|
||||||
|
fi
|
||||||
|
DONE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
LEVEL=$((LEVEL-1))
|
||||||
|
SELECTION=$PREVSELECTION
|
||||||
|
STARTDIR="$BASEDIR/$SELECTION"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
|
101
.local/bin/fzf/fzf_powermenu
Executable file
101
.local/bin/fzf/fzf_powermenu
Executable file
@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# fzf_powermenu
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 10 March 2021, 12:34:47 PM [GMT+1]
|
||||||
|
#Modified:
|
||||||
|
# Tue 01 November 2022, 06:45:08 AM [GMT]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# fzf
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
getuptime() {
|
||||||
|
uptime -p >/dev/null 2>&1
|
||||||
|
|
||||||
|
if [ "$?" -eq 0 ]; then
|
||||||
|
# Supports most Linux distro
|
||||||
|
# when the machine is up for less than '0' minutes then
|
||||||
|
# 'uptime -p' returns ONLY 'up', so we need to set a default value
|
||||||
|
UP_SET_OR_EMPTY=$(uptime -p | awk -F 'up ' '{print $2}')
|
||||||
|
UP=${UP_SET_OR_EMPTY:-'less than a minute'}
|
||||||
|
else
|
||||||
|
# Supports Mac OS X, Debian 7, etc
|
||||||
|
UP=$(uptime | sed -E 's/^[^,]*up *//; s/mins/minutes/; s/hrs?/hours/;
|
||||||
|
s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/;
|
||||||
|
s/^1 hours/1 hour/; s/ 1 hours/ 1 hour/;
|
||||||
|
s/min,/minutes,/; s/ 0 minutes,/ less than a minute,/; s/ 1 minutes/ 1 minute/;
|
||||||
|
s/ / /; s/, *[[:digit:]]* users?.*//')
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$UP"
|
||||||
|
}
|
||||||
|
|
||||||
|
asksetting() {
|
||||||
|
options=" Lock
|
||||||
|
望 Sleep
|
||||||
|
Logout
|
||||||
|
Restart
|
||||||
|
襤 Shutdown"
|
||||||
|
|
||||||
|
echo -e "Uptime: $(getuptime)
|
||||||
|
$options" | fzf --prompt="Power Settings: " --border=rounded --margin=1% --color=dark --height 100% --reverse --header=" POWER MENU " --info=hidden --header-first
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerFunction() {
|
||||||
|
init_system="$(cat /proc/1/comm)"
|
||||||
|
if [[ $init_system = "systemd" ]]; then
|
||||||
|
systemctl "$1"
|
||||||
|
elif [[ $init_system = "init" ]]; then
|
||||||
|
loginctl "$1"
|
||||||
|
elif [[ $init_system = "runit" ]]; then
|
||||||
|
loginctl "$1"
|
||||||
|
else
|
||||||
|
systemctl "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
LOOPSETTING="true"
|
||||||
|
while [ -n "$LOOPSETTING" ]; do
|
||||||
|
CHOICE="$(asksetting "$@")"
|
||||||
|
[ -n "$CHOICE" ] || exit
|
||||||
|
unset LOOPSETTING
|
||||||
|
case "$CHOICE" in
|
||||||
|
*Logout)
|
||||||
|
if [[ "$DESKTOP_SESSION" == "i3" ]]; then
|
||||||
|
i3-msg exit
|
||||||
|
elif [[ "$DESKTOP_SESSION" == "herbstluftwm" ]]; then
|
||||||
|
herbstclient quit
|
||||||
|
else
|
||||||
|
pkill -KILL -u "$USER"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*Lock)
|
||||||
|
multimonitorlock -l -- --time-str="%I:%M:%S %p"
|
||||||
|
;;
|
||||||
|
*Shutdown)
|
||||||
|
triggerFunction poweroff
|
||||||
|
;;
|
||||||
|
*Restart)
|
||||||
|
triggerFunction reboot
|
||||||
|
;;
|
||||||
|
*Sleep)
|
||||||
|
triggerFunction suspend
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Program terminated." && exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
138
.local/bin/fzf/fzf_run
Executable file
138
.local/bin/fzf/fzf_run
Executable file
@ -0,0 +1,138 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# fzf_run
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 10 March 2021, 12:34:47 PM [GMT+1]
|
||||||
|
#Modified:
|
||||||
|
# Sat 05 November 2022, 04:25:20 PM [GMT]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# bash
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
## Script metadata
|
||||||
|
SCRIPTNAME=${0##*/}
|
||||||
|
DESCRIPTION="A bash and fzf run script for terminal."
|
||||||
|
AUTHOR="The-Repo-Club <wayne6324@gmail.com>"
|
||||||
|
|
||||||
|
command=''
|
||||||
|
width=''
|
||||||
|
height=''
|
||||||
|
instance=''
|
||||||
|
term=''
|
||||||
|
|
||||||
|
#=== COLORS ===================================================================
|
||||||
|
|
||||||
|
## Set Colors (copied from makepkg)
|
||||||
|
b_blk="\e[0;90m"
|
||||||
|
b_red="\e[0;91m"
|
||||||
|
b_grn="\e[0;92m"
|
||||||
|
b_yel="\e[0;93m"
|
||||||
|
b_blu="\e[0;94m"
|
||||||
|
b_mag="\e[0;95m"
|
||||||
|
b_cyn="\e[0;96m"
|
||||||
|
b_wht="\e[0;97m"
|
||||||
|
blk="\e[0;30m"
|
||||||
|
red="\e[0;31m"
|
||||||
|
grn="\e[0;32m"
|
||||||
|
yel="\e[0;33m"
|
||||||
|
blu="\e[0;34m"
|
||||||
|
mag="\e[0;35m"
|
||||||
|
cyn="\e[0;36m"
|
||||||
|
wht="\e[0;37m"
|
||||||
|
bld="\e[1;1m"
|
||||||
|
del="\e[0;0m"
|
||||||
|
|
||||||
|
readonly b_blk b_red b_grn b_yel b_blu b_mag b_cyn b_wht blk red grn yel blu mag cyn wht bld del
|
||||||
|
|
||||||
|
#=== FUNCTION =================================================================
|
||||||
|
# Name: error
|
||||||
|
# Description: Print message with a red pretag an ERROR
|
||||||
|
# Parameter 1: Message to print
|
||||||
|
#==============================================================================
|
||||||
|
|
||||||
|
# copied from makepkg
|
||||||
|
error() {
|
||||||
|
local mesg=$1
|
||||||
|
printf "${red}${bld}==> ERROR:${wht} %b${del}\n" "$mesg"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_program() {
|
||||||
|
if command -v $command &>/dev/null; then
|
||||||
|
cmd="xterm"
|
||||||
|
if [[ -n $term ]]; then
|
||||||
|
cmd="$term"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $instance ]]; then
|
||||||
|
cmd+=" --class=$instance"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $command ]]; then
|
||||||
|
cmd+=" -e $command"
|
||||||
|
fi
|
||||||
|
exec $cmd >&/dev/null
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
printf "
|
||||||
|
%b
|
||||||
|
Usage:
|
||||||
|
%b...
|
||||||
|
%b [options]...
|
||||||
|
Options:
|
||||||
|
-h, --help Display help.
|
||||||
|
-c, --command Set the command to be ran..
|
||||||
|
-i, --instance Set the WM_CLASS for the program thats ran.
|
||||||
|
-t, --term Override the terminal you would like to use.
|
||||||
|
\n" "$DESCRIPTION" "$SCRIPTNAME" "$SCRIPTNAME"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ "$1" ]]; do
|
||||||
|
case $1 in
|
||||||
|
--command | -c)
|
||||||
|
shift
|
||||||
|
command=$1
|
||||||
|
;;
|
||||||
|
--instance | -i)
|
||||||
|
shift
|
||||||
|
instance=$1
|
||||||
|
;;
|
||||||
|
--term | -t)
|
||||||
|
shift
|
||||||
|
term=$1
|
||||||
|
;;
|
||||||
|
--help | -h)
|
||||||
|
show_help
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
error 'Incorrect option(s) specified.'
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error 'Incorrect option(s) specified.'
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
run_program
|
||||||
|
error 'Incorrect option(s) specified.'
|
||||||
|
show_help
|
||||||
|
|
269
.local/bin/fzf/fzf_youtube_subs
Executable file
269
.local/bin/fzf/fzf_youtube_subs
Executable file
@ -0,0 +1,269 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# fzf_youtube_subs
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Sun 03 January 2021, 05:09:33 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Thu 20 October 2022, 03:27:18 PM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# Watch your youtube subscriptions without a youtube account
|
||||||
|
# via curl, fzf, browser and basic unix commands.
|
||||||
|
#
|
||||||
|
# The $SUBS_FILE is a text file containing usernames or channel IDs
|
||||||
|
# comments and blank lines are ignored.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# fzf
|
||||||
|
#
|
||||||
|
|
||||||
|
fzf_menu() {
|
||||||
|
fzf --prompt="Select a video: " --border=rounded --margin=1% --color=dark --height 100% --reverse --header=" YOUTUBE SUBS MENU " --info=hidden --header-first
|
||||||
|
}
|
||||||
|
|
||||||
|
# -/-/-/-/- Settings -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
||||||
|
: "${SUBS_FILE:=${HOME}/.config/fzf/subs.ini}"
|
||||||
|
: "${SUBS_MENU_PROG:=fzf_menu}"
|
||||||
|
: "${SUBS:=${HOME}/.cache/subs}"
|
||||||
|
: "${SUBS_LINKS:=$SUBS/links}"
|
||||||
|
: "${SUBS_OPEN:=$(gobble videoplayer)}"
|
||||||
|
: "${SUBS_CACHE:=$SUBS/cache}"
|
||||||
|
: "${SUBS_SLEEP_VALUE:=0.05}" # raise this if you experience problems
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SEP=^^^^^ # shouldn't need to change this
|
||||||
|
# -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf >&2 '%s\n' "$*"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
die 'Usage: fzf_youtube_subs [-c cat_subs] [-g gen_links] [-u update_subs] [-d daemonize]'
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Synopsis: $SUBS_FILE [txt] -> $SUBS_LINKS [xml links]
|
||||||
|
#
|
||||||
|
# Updates local cache of xml subscription links from the
|
||||||
|
# subscription file containing either usernames or channel ids.
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
gen_links() {
|
||||||
|
: >"$SUBS_LINKS"
|
||||||
|
|
||||||
|
count=0
|
||||||
|
total=$(sed -e '/^$/d' -e '/^#/d' <"$SUBS_FILE" | wc -l)
|
||||||
|
|
||||||
|
while read -r line; do
|
||||||
|
|
||||||
|
# ignore comments and blank lines
|
||||||
|
case $line in '' | ' ' | '#'*) continue ;; esac
|
||||||
|
|
||||||
|
# strip off in-line comments and any trailing whitespace
|
||||||
|
line=${line%%#*}
|
||||||
|
line=${line%% *}
|
||||||
|
|
||||||
|
count=$((count + 1))
|
||||||
|
|
||||||
|
case $line in
|
||||||
|
UC*)
|
||||||
|
# YT channel IDs always begin with 'UC' and are 24 chars long
|
||||||
|
printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line"
|
||||||
|
|
||||||
|
[ ${#line} -eq 24 ] &&
|
||||||
|
printf 'https://youtube.com/feeds/videos.xml?%s\n' \
|
||||||
|
"channel_id=$line" >>"$SUBS_LINKS"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# otherwise we are given a username, we must find out its channel ID
|
||||||
|
printf "fetching channel ID for %s...\n" "$line"
|
||||||
|
|
||||||
|
curl -sfL --retry 10 "https://youtube.com/user/$line/about" |
|
||||||
|
while read -r line; do
|
||||||
|
case $line in
|
||||||
|
*channel/UC??????????????????????*)
|
||||||
|
line=${line##*channel/}
|
||||||
|
line=${line%%\"*}
|
||||||
|
printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line"
|
||||||
|
printf 'https://youtube.com/feeds/videos.xml?channel_id=%s\n' \
|
||||||
|
"$line" >>"$SUBS_LINKS"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done &
|
||||||
|
sleep "${SUBS_SLEEP_VALUE:-0}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
done <"$SUBS_FILE"
|
||||||
|
|
||||||
|
count=0
|
||||||
|
while [ "$count" -ne "$total" ]; do
|
||||||
|
count=$(wc -l <"$SUBS_LINKS")
|
||||||
|
printf "[%s/%s] waiting for jobs to complete...\n" "$count" "$total"
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Synopsis: $1 [LINK] -> $SUBS_CACHE/$chan_name/concat [CHANNEL INFO]
|
||||||
|
#
|
||||||
|
# Takes a channel rss feed link and creates a file
|
||||||
|
# with a line of its videos dates, titles, and urls.
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
get_vids() {
|
||||||
|
data=$(curl -sfL --retry 15 "$1")
|
||||||
|
|
||||||
|
# hide the first <published> tag which is the channel
|
||||||
|
# creation date
|
||||||
|
data=${data#*\<\/published\>}
|
||||||
|
|
||||||
|
# trim off outer <name> tags
|
||||||
|
chan=${data%%</name*}
|
||||||
|
chan=${chan##*name>}
|
||||||
|
|
||||||
|
printf "%s\n" "$data" |
|
||||||
|
while read -r line; do
|
||||||
|
case $line in
|
||||||
|
*'link rel='*)
|
||||||
|
line=${line#*href=\"}
|
||||||
|
line=${line%\"/\>}
|
||||||
|
line=https://${line#*www.}
|
||||||
|
url=$line
|
||||||
|
;;
|
||||||
|
*'<published>'*)
|
||||||
|
line=${line%+00:*}
|
||||||
|
line=${line#*<published>}
|
||||||
|
date=$line
|
||||||
|
;;
|
||||||
|
*'<media:title>'*)
|
||||||
|
line=${line%</*}
|
||||||
|
line=${line#*:title>}
|
||||||
|
title=$line
|
||||||
|
printf '%s\n' \
|
||||||
|
"${date}${SEP}${chan}${SEP}${title}${SEP}${url}" \
|
||||||
|
>>"$SUBS_CACHE/$chan"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Updates the local cache of subscriptions. ([-u] flag)
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
update_subs() {
|
||||||
|
[ -f "$SUBS_LINKS" ] || die 'Subs links have not been generated.'
|
||||||
|
|
||||||
|
rm -r "${SUBS_CACHE:-?}" 2>/dev/null || :
|
||||||
|
mkdir -p "$SUBS_CACHE"
|
||||||
|
|
||||||
|
total=$(wc -l <"$SUBS_LINKS")
|
||||||
|
|
||||||
|
count=0
|
||||||
|
while read -r link; do
|
||||||
|
count=$((count + 1))
|
||||||
|
printf 'starting job [%s/%s] for %s\n' "$count" "$total" "$link"
|
||||||
|
get_vids "$link" &
|
||||||
|
sleep "${SUBS_SLEEP_VALUE:-0}"
|
||||||
|
done <"$SUBS_LINKS"
|
||||||
|
|
||||||
|
count=0
|
||||||
|
while [ "$count" -ne "$total" ]; do
|
||||||
|
count=$(printf '%s\n' "$SUBS_CACHE"/* | wc -l)
|
||||||
|
printf "[%s/%s] waiting for fetch jobs to complete...\n" "$count" "$total"
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s\n\n' 'done!'
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Grab current cache of subscriptions, sort by date uploaded
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
cat_subs() {
|
||||||
|
sort -r "$SUBS_CACHE"/* |
|
||||||
|
while read -r line; do
|
||||||
|
chan=${line#*$SEP}
|
||||||
|
chan=${chan%%$SEP*}
|
||||||
|
title=${line#*$chan$SEP}
|
||||||
|
title=${title%%$SEP*}
|
||||||
|
date=${line%%$SEP*}
|
||||||
|
date=${date#*-}
|
||||||
|
date=${date%T*}
|
||||||
|
printf '[%s %s] %s\n' "$date" "$chan" "$title"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Split the concatenated lines into entities, send to menu program.
|
||||||
|
# Finally, play the result with mpv.
|
||||||
|
get_sel() {
|
||||||
|
if [ -d "$SUBS_CACHE" ]; then
|
||||||
|
sel=$(cat_subs | $SUBS_MENU_PROG)
|
||||||
|
else
|
||||||
|
die 'Subs cache has not been retrieved.'
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$sel" ] || die Interrupted
|
||||||
|
|
||||||
|
chan="${sel#* }"
|
||||||
|
chan="${chan%%] *}"
|
||||||
|
title=${sel#*"$chan"\] }
|
||||||
|
while read -r line; do
|
||||||
|
case $line in
|
||||||
|
*"$SEP$title$SEP"*)
|
||||||
|
url=${line##*$SEP}
|
||||||
|
if [ "$url" ]; then
|
||||||
|
printf 'playing: %s\n' "$url"
|
||||||
|
# Play the selection.
|
||||||
|
# shellcheck disable=2086
|
||||||
|
exec devour $SUBS_OPEN "$url"
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <"$SUBS_CACHE/$chan"
|
||||||
|
}
|
||||||
|
|
||||||
|
daemonize() {
|
||||||
|
# create a cached copy of the subs file to check for changes
|
||||||
|
# if changes occur, re-generate links automatically
|
||||||
|
daemon_file=${HOME}/.cache/subs_daemon.cache
|
||||||
|
if [ ! -f "$daemon_file" ]; then
|
||||||
|
cp -f "${SUBS_FILE:=${HOME}/.config/fzf/subs.ini}" "$daemon_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if ! cmp "${SUBS_FILE:=${HOME}/.config/fzf/subs.ini}" "$daemon_file"; then
|
||||||
|
cp -f "${SUBS_FILE:=${HOME}/.config/fzf/subs.ini}" "$daemon_file"
|
||||||
|
fi
|
||||||
|
gen_links
|
||||||
|
update_subs
|
||||||
|
interval=${SUBS_DAEMON_INTERVAL:-$((10 * 60))}
|
||||||
|
printf 'Sleeping for %s seconds...\n' "$interval"
|
||||||
|
sleep "$interval"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
mkdir -p "$SUBS"
|
||||||
|
|
||||||
|
case ${1#-} in
|
||||||
|
h) usage ;;
|
||||||
|
g) gen_links ;;
|
||||||
|
u) update_subs ;;
|
||||||
|
c) cat_subs ;;
|
||||||
|
d) daemonize ;;
|
||||||
|
*) get_sel ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
125
.local/bin/rofi/README.md
Normal file
125
.local/bin/rofi/README.md
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# Bookmark Manager
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```help
|
||||||
|
Usage: ./bm [modifier(s)] command [option(s)]
|
||||||
|
|
||||||
|
Commands :
|
||||||
|
==========
|
||||||
|
-h Print this help
|
||||||
|
-H Print help for legacy usage
|
||||||
|
-v/-V Print the version
|
||||||
|
-a 'URL' Add the URL to bookmark file
|
||||||
|
Options for -a
|
||||||
|
-t "TagList" Tags are sparated by a comma ,
|
||||||
|
-T "Title" Title for this URL (if empty and allowed Title
|
||||||
|
downloaded)
|
||||||
|
-A "accel" Accelerator when search for URLs (start with Accel)
|
||||||
|
Default is FQDN without gTld (and www, and sheme)
|
||||||
|
-F Force the bookmark to be created (even if duplicate
|
||||||
|
or invalid)
|
||||||
|
-p Force the screenshot to be taken
|
||||||
|
|
||||||
|
-l List all URLs (default action, same thing as calling bm without args)
|
||||||
|
Options for -l
|
||||||
|
-z Use the alternate print list
|
||||||
|
-n Sort the results by date
|
||||||
|
-N Sort the results by date (reverse)
|
||||||
|
|
||||||
|
|
||||||
|
-s 'object' Search for bookmarks
|
||||||
|
Options for -s
|
||||||
|
-i Incensitive case search
|
||||||
|
-z Use the alternate print list
|
||||||
|
-n Sort the results by date
|
||||||
|
-N Sort the results by date (reverse)
|
||||||
|
|
||||||
|
-o 'object' Search for bookmarks and open it (use the same argument as for -s)
|
||||||
|
Options for -o
|
||||||
|
-i Incensitive case search
|
||||||
|
-O If more than one answer force the first bookmark to be open
|
||||||
|
-Y If more than one answer force ALL bookmarks to be open
|
||||||
|
|
||||||
|
-x 'object' Search for bookmarks and copy it to clipboard (use the same argument as for -s)
|
||||||
|
Options for -x
|
||||||
|
-i Incensitive case search
|
||||||
|
-X If more than one answer force the first bookmark to be copied
|
||||||
|
-Y If more than one answer force ALL bookmarks to be copied
|
||||||
|
|
||||||
|
-r 'object' Search for bookmarks and Print the recorded associated picture (use the same argument as for -s)
|
||||||
|
Options for -r
|
||||||
|
-i Incensitive case search
|
||||||
|
-O If more than one answer force the first bookmark to have its picture printed
|
||||||
|
-Y If more than one answer force ALL bookmarks to have their picture printed
|
||||||
|
|
||||||
|
-d 'URL' or Delete the URL from bookmark file md5sum or 'URL part'
|
||||||
|
Options for -d
|
||||||
|
-D Delete first occurence only
|
||||||
|
-F Force the bookmark to be deleted (even if duplicate)
|
||||||
|
-p Delete the associated picture (no trash available)
|
||||||
|
|
||||||
|
-g Generate a HTML page with all bookmarks
|
||||||
|
If used more than once, generate a page per tag
|
||||||
|
Options for -g
|
||||||
|
-G "filename" If g==1 then generate then use this filename to generate page
|
||||||
|
-O Open the file when generated
|
||||||
|
|
||||||
|
-P 'object' Generate all Non existant picture (check done for all URL in bm), if none argument.
|
||||||
|
If an argument is given (use the same argument as for -s) only the results will have a picture.
|
||||||
|
Options for -P
|
||||||
|
-F Force the picture to be taken again (even if already exists).
|
||||||
|
|
||||||
|
-L List all tags
|
||||||
|
|
||||||
|
-C Print the color table (usefull for configuration)
|
||||||
|
|
||||||
|
-S Show statistics about bookmarks/tags (and also configuration)
|
||||||
|
Options for -S
|
||||||
|
-p Print the list of orphaned Pictures
|
||||||
|
|
||||||
|
Modifiers :
|
||||||
|
===========
|
||||||
|
-c 'file' Use this configuration file instead of the default one
|
||||||
|
|
||||||
|
-b 'file' Use this bookmark file instead of the default one
|
||||||
|
|
||||||
|
Objects:
|
||||||
|
========
|
||||||
|
:string Search in accelerator list
|
||||||
|
+string Search in tags list
|
||||||
|
=string Search in MD5 list
|
||||||
|
/string Search in URL list
|
||||||
|
string Search in full test
|
||||||
|
|
||||||
|
How to use:
|
||||||
|
===========
|
||||||
|
# add a bookmark with the given url, description, and optional tags
|
||||||
|
$ bm add <url> [desc] [tag...] [accelerator]
|
||||||
|
|
||||||
|
# open the first bookmark matching <query>
|
||||||
|
$ bm open <query>
|
||||||
|
$ bm <query>
|
||||||
|
|
||||||
|
# search the bookmarks via full-text <query>
|
||||||
|
$ bm search <query>
|
||||||
|
|
||||||
|
# list tags
|
||||||
|
$ bm tags
|
||||||
|
|
||||||
|
# list bookmarks available
|
||||||
|
$ bm list
|
||||||
|
$ bm ls
|
||||||
|
$ bm
|
||||||
|
|
||||||
|
# display statistics about the bookmarks
|
||||||
|
$ bm statistics
|
||||||
|
$ bm stats
|
||||||
|
|
||||||
|
# view bookmark screenshots in your default browser
|
||||||
|
$ bm view design
|
||||||
|
$ bm view
|
||||||
|
|
||||||
|
# clear all bookmarks
|
||||||
|
$ bm clear
|
||||||
|
```
|
925
.local/bin/rofi/bm
Executable file
925
.local/bin/rofi/bm
Executable file
@ -0,0 +1,925 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# bm
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Fri 09 December 2022, 07:19:23 AM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Fri 14 July 2023, 11:47:25 PM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# <None>
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
# default filename for Baker config file (just the filename no path)
|
||||||
|
readonly _BM_DEFAULT_CONFIG_FILENAME="${_BM_DEFAULT_CONFIG_FILENAME:=bm.conf}"
|
||||||
|
|
||||||
|
config() {
|
||||||
|
echo "Reading Configuration..."
|
||||||
|
[[ "${__w:=$HOME}" != "${HOME}" ]] && echo "Working dir changed to ${__w}"
|
||||||
|
[[ "${__w: -1}" != '/' ]] && __w="${__w//\/\//\/}/"
|
||||||
|
if [ ${__B:=0} -eq 0 ]; then
|
||||||
|
# If a config file is given, we take it first, or look for a default config file
|
||||||
|
export _BM_CONFIG_FILE="${__w}${_BM_DEFAULT_CONFIG_FILENAME}"
|
||||||
|
if [ "$(uname -s)" = 'Linux' -a -f "$(readlink -m "${1:-}" 2>/dev/null)" ]; then
|
||||||
|
export _BM_CONFIG_FILE="${1}"
|
||||||
|
. "${1}"
|
||||||
|
elif [ -e "${1}" ]; then
|
||||||
|
export _BM_CONFIG_FILE="${1}"
|
||||||
|
. "${1}"
|
||||||
|
elif [ ${__n:=0} -eq 0 -a -f "${__w}${_BM_DEFAULT_CONFIG_FILENAME}" ]; then
|
||||||
|
. "${__w}${_BM_DEFAULT_CONFIG_FILENAME}"
|
||||||
|
echo "Default config file loaded ${__w}${_BM_DEFAULT_CONFIG_FILENAME}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
export _BM_CONFIG_FILE='none'
|
||||||
|
fi
|
||||||
|
# _BM_BOOKMARK_FILE : Where is stored the bookmark file
|
||||||
|
export _BM_BOOKMARK_FILE="${_BM_BOOKMARK_FILE:=${__w}bm.bm}"
|
||||||
|
|
||||||
|
# _BM_BOOKMARK_BACKUP_FILE : Where is stored the backup of bookmark file
|
||||||
|
export _BM_BOOKMARK_BACKUP_FILE="${_BM_BOOKMARK_BACKUP_FILE:=${__w}.bm.bm.bck}"
|
||||||
|
|
||||||
|
# _BM_HTML_FILE : Location of generated HTML file
|
||||||
|
export _BM_HTML_FILE="${_BM_HTML_FILE:=${__w}bm.html}"
|
||||||
|
|
||||||
|
# _BM_DELETE_FILE : Where to find the deleted URLs
|
||||||
|
export _BM_DELETE_FILE="${_BM_DELETE_FILE:=${__w}.bm.trash}"
|
||||||
|
|
||||||
|
# _BM_BOOKMARK_BACKUP : Copy the bookmark file before each modification
|
||||||
|
export _BM_BOOKMARK_BACKUP=${_BM_BOOKMARK_BACKUP:=false}
|
||||||
|
|
||||||
|
# _BM_BOOKMARK_BACKUP_VERSION : How many copy to keep (0 for 1, 1 for 2, ...)
|
||||||
|
export _BM_BOOKMARK_BACKUP_VERSION=${_BM_BOOKMARK_BACKUP_VERSION:=0}
|
||||||
|
|
||||||
|
# _BM_DELETE_ALLOWED : Even with the force flag the delete function will not be allowed (true or false)
|
||||||
|
export _BM_DELETE_ALLOWED=${_BM_DELETE_ALLOWED:=true}
|
||||||
|
|
||||||
|
# _BM_DELETE_TO_FILE : Allow or disallow the deleted URLs to be copied in the Trash (true or false)
|
||||||
|
export _BM_DELETE_TO_FILE=${_BM_DELETE_TO_FILE:=false}
|
||||||
|
|
||||||
|
# _BM_DELETE_PICTURE : Allow or disallow the picture associated to a URL to be deleted (true or false)
|
||||||
|
export _BM_DELETE_PICTURE=${_BM_DELETE_PICTURE:=true}
|
||||||
|
|
||||||
|
# _BM_DUPS_DETECT : Look for duplicates URL before insert in bookmark file (ovverided by -F)
|
||||||
|
export _BM_DUPS_DETECT=${_BM_DUPS_DETECT:=true}
|
||||||
|
|
||||||
|
# _BM_ASK_BEFORE_OPEN : When using bm with a URL (or search term) ask before opening the browser if more than one answer
|
||||||
|
export _BM_ASK_BEFORE_OPEN=${_BM_ASK_BEFORE_OPEN:=true}
|
||||||
|
|
||||||
|
# _BM_GET_PAGE_TITLE : When adding a URL execute a curl command to get the <title> if none given
|
||||||
|
export _BM_GET_PAGE_TITLE=${_BM_GET_PAGE_TITLE:=true}
|
||||||
|
|
||||||
|
# _BM_SCREENSHOT_DIRECTORY : Where to store the screenshot taken
|
||||||
|
export _BM_SCREENSHOT_DIRECTORY="${_BM_SCREENSHOT_DIRECTORY:=${__w}.bm.shots}"
|
||||||
|
|
||||||
|
# _BM_SCREENSHOT_GET : When adding a URL try to take a screenshot in background
|
||||||
|
export _BM_SCREENSHOT_GET=${_BM_SCREENSHOT_GET:=true}
|
||||||
|
|
||||||
|
# _BM_SCREENSHOT_WAIT : When taking a screenshot wait for the action to finish
|
||||||
|
export _BM_SCREENSHOT_WAIT=${_BM_SCREENSHOT_WAIT:=false}
|
||||||
|
|
||||||
|
# _BM_OPEN_FIRST : When using open, the first link found is open
|
||||||
|
# Also work with the copy to clipboard function
|
||||||
|
export _BM_OPEN_FIRST=${_BM_OPEN_FIRST:=false}
|
||||||
|
|
||||||
|
# _BM_OPEN_ALL : When using open, if more than one result, all are open (with the same function)
|
||||||
|
# Also work with the copy to clipboard function (overrided by -Y)
|
||||||
|
export _BM_OPEN_ALL=${_BM_OPEN_ALL:=false}
|
||||||
|
|
||||||
|
# _BM_NO_ARGS_FORCE_HELP : If no args given to script sends to help instead of list all link
|
||||||
|
export _BM_NO_ARGS_FORCE_HELP=${_BM_NO_ARGS_FORCE_HELP:=false}
|
||||||
|
|
||||||
|
# _BM_CREATE_BOOKMARK_FILE : If bm is called to list and no file exists. bm will create a default one.
|
||||||
|
export _BM_CREATE_BOOKMARK_FILE=${_BM_CREATE_BOOKMARK_FILE:=true}
|
||||||
|
|
||||||
|
# _BM_CMD_CAPTURE_CHECK : If you don't have tool installed to take screenshot, just put to false.
|
||||||
|
# It will disable the _BM_GET_SCREENSHOT.
|
||||||
|
export _BM_CMD_CAPTURE_CHECK=${_BM_CMD_CAPTURE_CHECK:=true}
|
||||||
|
|
||||||
|
# I suppose the uname command is available
|
||||||
|
# Please note, the PRE & POST capture command will not be analyzes nor verified.
|
||||||
|
if [ "$(uname -s)" = 'Linux' ]; then
|
||||||
|
export _BM_CMD_IMAGE_OPEN='feh'
|
||||||
|
export _BM_CMD_OPEN='xdg-open'
|
||||||
|
export _BM_CMD_PRE_CAPTURE=''
|
||||||
|
export _BM_CMD_CAPTURE='cutycapt'
|
||||||
|
export _BM_CMD_CAPTURE_ARGS='--insecure --url={URL} --out={FILE}'
|
||||||
|
export _BM_CMD_POST_CAPTURE=''
|
||||||
|
export _BM_CMD_MD5='md5sum'
|
||||||
|
export _BM_CMD_COPY='xsel'
|
||||||
|
else
|
||||||
|
# To keep commpatibility with the Mac (even i don't have any)
|
||||||
|
export _BM_CMD_IMAGE_OPEN='open'
|
||||||
|
export _BM_CMD_OPEN='open'
|
||||||
|
export _BM_CMD_PRE_CAPTURE=''
|
||||||
|
export _BM_CMD_CAPTURE='webkit2png'
|
||||||
|
export _BM_CMD_CAPTURE_ARGS='-C -o {FILE}.png {URL}'
|
||||||
|
export _BM_CMD_POST_CAPTURE='cp -- "{FILE}-clipped.png" "{FILE}" ; rm -f -- {FILE}-{clipped,full,thumb}.png'
|
||||||
|
export _BM_CMD_MD5='md5'
|
||||||
|
export _BM_CMD_COPY='xsel'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# _BM_PRINT_LINE : This line is used to print datas
|
||||||
|
export _BM_PRINT_LINE="${_BM_PRINT_LINE:=${BOLD}${GRAY}BM_ACCEL ${RESET}${DIM}-->${RESET} ${GRAY_LIGHT}BM_TITLE ${GRAY_DARK}${BOLD}[BM_TAGS]${RESET}\n${UNDERLINE}${DIM}BM_URL${RESET} - ${GRAY_DARK}Added: BM_DATE${RESET}}"
|
||||||
|
|
||||||
|
# _BM_PRINT_LINE_ALTERNATIVE : This line is used to print datas
|
||||||
|
export _BM_PRINT_LINE_ALTERNATIVE="${_BM_PRINT_LINE_ALTERNATIVE:=${BOLD}${GRAY}BM_ACCEL ${RESET}${DIM}-->${RESET} ${GRAY_LIGHT}BM_TITLE ${GRAY_DARK}${BOLD}[BM_TAGS]${RESET}\n${UNDERLINE}${DIM}BM_URL${RESET} ${GRAY_DARK}(BM_MD5)${RESET} - ${GRAY_DARK}Added: BM_DATE${RESET} - ${GRAY_DARK}Picture: BM_PICTURE${RESET}}"
|
||||||
|
|
||||||
|
# _BM_PRINT_SCHEME : If set to no, bm will remove scheme from URL printing
|
||||||
|
export _BM_PRINT_SCHEME=${_BM_PRINT_SCHEME:=true}
|
||||||
|
|
||||||
|
# _BM_SEARCH_IGNORECASE : Ignore case if set when searching (overrided by -i)
|
||||||
|
export _BM_SEARCH_IGNORECASE=${_BM_SEARCH_IGNORECASE:=false}
|
||||||
|
}
|
||||||
|
|
||||||
|
# slug creates a friendly URL like 'hello-world'
|
||||||
|
slug() {
|
||||||
|
iconv -f utf8 -t ascii//TRANSLIT | tr -cs '[:alnum:]\n' - | tr '[:upper:]' '[:lower:]' | sed 's|^-*||;s|-*$||'
|
||||||
|
}
|
||||||
|
|
||||||
|
# version
|
||||||
|
RELEASE='$Format:%h$'
|
||||||
|
VERSION='2021.04.05'
|
||||||
|
|
||||||
|
#
|
||||||
|
# Output usage info
|
||||||
|
#
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
Usage: ${0} [modifier(s)] command [option(s)]
|
||||||
|
|
||||||
|
Commands :
|
||||||
|
==========
|
||||||
|
-h Print this help
|
||||||
|
-v/-V Print the version/And the short commit name
|
||||||
|
-a 'URL' Add the URL to bookmark file
|
||||||
|
Options for -a
|
||||||
|
-t "TagList" Tags are sparated by a comma ,
|
||||||
|
-T "Title" Title for this URL (if empty and allowed Title
|
||||||
|
downloaded)
|
||||||
|
-A "accel" Accelerator when search for URLs (start with Accel)
|
||||||
|
Default is an autoincrement index
|
||||||
|
-F Force the bookmark to be created (even if duplicate
|
||||||
|
or invalid)
|
||||||
|
-p Force the screenshot to be taken (config dependant)
|
||||||
|
|
||||||
|
-l List all URLs (default action, same thing as calling bm without args)
|
||||||
|
Options for -l
|
||||||
|
-z Use the alternate print list
|
||||||
|
-n Sort the results by date
|
||||||
|
-N Sort the results by date (reverse)
|
||||||
|
|
||||||
|
-s 'object' Search for bookmarks
|
||||||
|
Options for -s
|
||||||
|
-i Incensitive case search
|
||||||
|
-z Use the alternate print list
|
||||||
|
-n Sort the results by date
|
||||||
|
-N Sort the results by date (reverse)
|
||||||
|
Objects are
|
||||||
|
:string Search in accelerator list
|
||||||
|
+string Search in tags list
|
||||||
|
=string Search in MD5 list
|
||||||
|
/string Search in URL list
|
||||||
|
string Search in full test
|
||||||
|
|
||||||
|
-o 'object' Search for bookmarks and open it (use the same argument as for -s)
|
||||||
|
Options for -o
|
||||||
|
-i Incensitive case search
|
||||||
|
-O If more than one answer force the first bookmark to be open
|
||||||
|
-Y If more than one answer force ALL bookmarks to be open
|
||||||
|
|
||||||
|
-x 'object' Search for bookmarks and copy it to clipboard (use the same argument as for -s)
|
||||||
|
Options for -x
|
||||||
|
-i Incensitive case search
|
||||||
|
-X If more than one answer force the first bookmark to be copied
|
||||||
|
-Y If more than one answer force ALL bookmarks to be copied
|
||||||
|
|
||||||
|
-r 'object' Search for bookmarks and Print the recorded associated picture (use the same argument as for -s)
|
||||||
|
Options for -r
|
||||||
|
-i Incensitive case search
|
||||||
|
-O If more than one answer force the first bookmark to have its picture printed
|
||||||
|
-Y If more than one answer force ALL bookmarks to have their picture printed
|
||||||
|
|
||||||
|
-d 'URL' Delete the URL from bookmark file
|
||||||
|
md5sum or
|
||||||
|
'URL part' Options for -d
|
||||||
|
-D Delete first occurence only
|
||||||
|
-F Force the bookmark to be deleted (even if duplicate)
|
||||||
|
-p Delete the associated picture (no trash available)
|
||||||
|
|
||||||
|
-g Generate a HTML page with all bookmarks
|
||||||
|
If used more than once, generate a page per tag
|
||||||
|
Options for -g
|
||||||
|
-G "filename" If g==1 then generate and use this filename to generate page
|
||||||
|
-O Open the file when generated
|
||||||
|
|
||||||
|
-P 'object' Generate all Non existant picture (check done for all URL in bm), if none argument.
|
||||||
|
If an argument is given (use the same argument as for -s) only the results will have a picture.
|
||||||
|
Options for -P
|
||||||
|
-F Force the picture to be taken again (even if already exists).
|
||||||
|
|
||||||
|
-q 'object' Connect to the URL to replace the existing Title by the downloaded one.
|
||||||
|
Options for -q
|
||||||
|
-i Incensitive case search
|
||||||
|
-O If more than one answer force the first bookmark to have its title downloaded
|
||||||
|
-Y If more than one answer force ALL bookmarks to have their title downloaded
|
||||||
|
|
||||||
|
-L List all tags
|
||||||
|
|
||||||
|
-C Print the color table (usefull for configuration)
|
||||||
|
|
||||||
|
-E Open the bm.bm with your \$EDITOR
|
||||||
|
|
||||||
|
-S Show statistics about bookmarks/tags (and also configuration)
|
||||||
|
Options for -S
|
||||||
|
-p Print the list of orphaned Pictures
|
||||||
|
|
||||||
|
Modifiers :
|
||||||
|
===========
|
||||||
|
-c 'file' Use this configuration file instead of the default one
|
||||||
|
|
||||||
|
-b 'file' Use this bookmark file instead of the default one
|
||||||
|
|
||||||
|
-w 'directory' Use this directory to find default configuration file and bookmark file instead of the default one
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccelMax() {
|
||||||
|
while read a; do
|
||||||
|
(( ${a:=0} >= ${_max:=0} )) && (( _max=${a}+1 ))
|
||||||
|
done <<< "$( awk -F'|' '$0 !~ /^[ ]*#/ && $3 ~ /^[:space:]*:[:space:]*[0-9]+[:space:]*$/ { sub(/:+/, "", $3); print $3; }' "${_BM_BOOKMARK_FILE}")"
|
||||||
|
echo -n "${_max}"
|
||||||
|
}
|
||||||
|
|
||||||
|
backupBm() {
|
||||||
|
if [ "${_BM_BOOKMARK_BACKUP,,}" = 'true' ]; then
|
||||||
|
while [ ${count:=${_BM_BOOKMARK_BACKUP_VERSION}} -gt 0 ]; do
|
||||||
|
cp -- "${_BM_BOOKMARK_BACKUP_FILE}.$(( count-1 ))" "${_BM_BOOKMARK_BACKUP_FILE}.$(( count-- ))" 2>&1 >/dev/null
|
||||||
|
done
|
||||||
|
cp -- "${_BM_BOOKMARK_FILE}" "${_BM_BOOKMARK_BACKUP_FILE}.0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
titleDl() {
|
||||||
|
if command -v hxselect &>/dev/null ; then
|
||||||
|
curl -Lks "${1:-}" 2>&1 | hxselect -ic title 2>/dev/null
|
||||||
|
else
|
||||||
|
curl -Lks "${1:-}" 2>&1 | sed '/<title>/I!d;/<\/title>/I!d;s;^[[:space:]]*<title>\([^<]*\)<.*;\1;i'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUrl() {
|
||||||
|
[[ "${__url}" =~ ^[[:space:]]*(f|ht)tps*://.*$ ]] || {
|
||||||
|
if [ ${__F:=0} -eq 0 ]; then
|
||||||
|
die "Your URL seems invalid '${__url}'. Use -F to force."
|
||||||
|
else
|
||||||
|
echo "Force adding invalid URL" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
local _sum="$(${_BM_CMD_MD5} <<< "${__url}")"
|
||||||
|
if [ "${_BM_DUPS_DETECT,,}" = 'true' ]; then
|
||||||
|
if [ ! -z "$(awk -F '|' '$0 !~ /^[ ]*#/ && $1 ~ /'"${_sum%% *}"'/' "${_BM_BOOKMARK_FILE}")" ]; then
|
||||||
|
if [ ${__F:=0} -eq 0 ]; then
|
||||||
|
die "URL is already in the Bookmark file"
|
||||||
|
else
|
||||||
|
echo "Force adding duplicate URL" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "${_BM_GET_PAGE_TITLE,,}" = 'true' -a -z "${__T:=}" ]; then
|
||||||
|
# if [ $( command -v hxselect &>/dev/null ) ]; then
|
||||||
|
# __T="$( curl -Lks "${__url}" 2>&1 | hxselect -ic title )"
|
||||||
|
# else
|
||||||
|
# __T="$( curl -Lks "${__url}" 2>&1 | sed '/<title>/I!d;/<\/title>/I!d;s;^[[:space:]]*<title>\([^<]*\)<.*;\1;i' )"
|
||||||
|
# fi
|
||||||
|
__T="$( titleDl "${__url}" )"
|
||||||
|
fi
|
||||||
|
[[ ! -z "${__T:=}" ]] && __T="${__T//\|/\\|}"
|
||||||
|
backupBm
|
||||||
|
[[ -z "${__T:=}" ]] && __T="$(slug <<< "${__url}")" # If no title => slug the url
|
||||||
|
[[ -z "${__t:=}" ]] && __t='default' # Default tag is default
|
||||||
|
[[ -z "${__A:=}" ]] && __A="$(getAccelMax)"
|
||||||
|
[[ "${__A:0:1}" != ':' ]] && __A=":${__A}" # If no accel => Add a number
|
||||||
|
echo "${_sum%% *}|$(date '+%FT%TZ')|${__A}|${__url}|${__T}|${__t}" >> "${_BM_BOOKMARK_FILE}" || die "Insert aborted into '${_BM_BOOKMARK_FILE}' !"
|
||||||
|
echo "${__url} inserted into '${_BM_BOOKMARK_FILE}'"
|
||||||
|
[[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${_sum%% *}.png" || ${__F:=0} -eq 1 ]] && { screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${_sum%% *}.png" "${__url}" & }
|
||||||
|
[[ "${_BM_SCREENSHOT_WAIT,,}" = 'true' ]] && wait
|
||||||
|
search_bookmarks "${__A}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# manage each line to print it
|
||||||
|
#
|
||||||
|
|
||||||
|
readLines() {
|
||||||
|
IFS='|'
|
||||||
|
while read m d a u T t; do
|
||||||
|
_line="${_BM_PRINT_LINE}"
|
||||||
|
[[ ${__z:=0} -eq 1 ]] && _line="${_BM_PRINT_LINE_ALTERNATIVE}"
|
||||||
|
_line="${_line//BM_MD5/$m}"
|
||||||
|
if [ "${_BM_PRINT_SCHEME,,}" = 'false' ]; then
|
||||||
|
u="$(sed 's/^[[:space:]]*\(f\|ht\)tps*:\/\///' <<< "${u}")"
|
||||||
|
fi
|
||||||
|
_line="${_line//BM_URL/$u}"
|
||||||
|
_line="${_line//BM_TITLE/$T}"
|
||||||
|
_line="${_line//BM_TAGS/$t}"
|
||||||
|
_line="${_line//BM_ACCEL/${a:=No-Accelerator}}"
|
||||||
|
_line="${_line//BM_DATE/${d}}"
|
||||||
|
local _pict="$(stat -c '%y' "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" 2>/dev/null)"
|
||||||
|
[[ ! -z "${_pict}" ]] && _pict="$(date '+%FT%TZ' --date "${_pict}")"
|
||||||
|
_line="${_line//BM_PICTURE/${_pict:=None}}"
|
||||||
|
echo -e "${_line}\n"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# List all bookmarks
|
||||||
|
#
|
||||||
|
|
||||||
|
list_bookmarks() {
|
||||||
|
[[ ${__n:=0} -eq 1 ]] && { awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | sort -t'|' -k2 | readLines; return; }
|
||||||
|
[[ ${__N:=0} -eq 1 ]] && { awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | sort -t'|' -k2 -r | readLines; return; }
|
||||||
|
awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | readLines
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Search all bookmarks with <query>
|
||||||
|
#
|
||||||
|
search() {
|
||||||
|
local _s=''
|
||||||
|
[[ -z "${1:-}" ]] && list_bookmarks && return
|
||||||
|
case "${1:0:1}" in
|
||||||
|
:) _s=3; _ss=0 ;; # Accelerator
|
||||||
|
+) _s="NF"; _ss=1 ;; # Tags
|
||||||
|
=) _s=1; _ss=1 ;; # MD5 part
|
||||||
|
/) _s=4; _ss=1 ;; # URL part
|
||||||
|
*) _s=0; _ss=0 ;; # full text search
|
||||||
|
esac
|
||||||
|
[[ "${_BM_SEARCH_IGNORECASE,,}" = 'true' || ${__i:=0} -eq 1 ]] && local _ign=1
|
||||||
|
awk -F'|' -vIgn=${_ign:=0} '
|
||||||
|
BEGIN {
|
||||||
|
if (Ign == 1) { s=tolower("'"${1:${_ss}}"'"); } else { s="'"${1:${_ss}}"'"; }
|
||||||
|
}
|
||||||
|
$0 !~ /^[ ]*#/ && (Ign == 0 && $'"${_s}"' ~ s) || (Ign ==1 && tolower($'"${_s}"') ~ s) {print $0;}
|
||||||
|
' "${_BM_BOOKMARK_FILE}"
|
||||||
|
|
||||||
|
}
|
||||||
|
search_bookmarks() {
|
||||||
|
[[ ${__n:=0} -eq 1 ]] && { search "${@}" | sort -t'|' -k2 | readLines; return; }
|
||||||
|
[[ ${__N:=0} -eq 1 ]] && { search "${@}" | sort -t'|' -k2 -r | readLines; return; }
|
||||||
|
search "${@}" | readLines
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Open first bookmark matching <query>
|
||||||
|
#
|
||||||
|
|
||||||
|
recorded_picture() {
|
||||||
|
local _lines="$(search "${@}")"
|
||||||
|
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
|
||||||
|
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
|
||||||
|
read -p"More than one URL found. Open all Pictures ? [Y/N] : " -n1 _answer
|
||||||
|
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
local _all=''
|
||||||
|
echo ''
|
||||||
|
while read _nl; do
|
||||||
|
readLines <<< "${_nl}"
|
||||||
|
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
|
||||||
|
_all="${_BM_SCREENSHOT_DIRECTORY}/$(awk -F'|' '{print $1}' <<< "${_nl}").png ${_all}"
|
||||||
|
else
|
||||||
|
"${_BM_CMD_IMAGE_OPEN}" "${_BM_SCREENSHOT_DIRECTORY}/$(awk -F'|' '{print $1}' <<< "${_nl}").png"
|
||||||
|
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
|
||||||
|
fi
|
||||||
|
done <<< "$(echo -e "${_lines}")"
|
||||||
|
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_IMAGE_OPEN}" "${_all}"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_title() {
|
||||||
|
local _lines="$(search "${@}")"
|
||||||
|
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
|
||||||
|
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
|
||||||
|
read -p"More than one URL found. Download Title for all URLs ? [Y/N] : " -n1 _answer
|
||||||
|
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
local _all=''
|
||||||
|
echo ''
|
||||||
|
while read _nl; do
|
||||||
|
readLines <<< "${_nl}"
|
||||||
|
local u="$(awk -F'|' '{print $4}' <<< "${_nl}")"
|
||||||
|
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
|
||||||
|
echo "${u} --> $( titleDl "${u}" )"
|
||||||
|
else
|
||||||
|
echo "${u} --> $( titleDl "${u}" )"
|
||||||
|
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
|
||||||
|
fi
|
||||||
|
done <<< "$(echo -e "${_lines}")"
|
||||||
|
}
|
||||||
|
|
||||||
|
open_bookmark() {
|
||||||
|
local _lines="$(search "${@}")"
|
||||||
|
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
|
||||||
|
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
|
||||||
|
read -p"More than one URL found. Open all ? [Y/N] : " -n1 _answer
|
||||||
|
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
local _all=''
|
||||||
|
while read _nl; do
|
||||||
|
readLines <<< "${_nl}"
|
||||||
|
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
|
||||||
|
_all="$(awk -F'|' '{print $4}' <<< "${_nl}") ${_all}"
|
||||||
|
else
|
||||||
|
"${_BM_CMD_OPEN}" "$(awk -F'|' '{print $4}' <<< "${_nl}")"
|
||||||
|
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
|
||||||
|
fi
|
||||||
|
done <<< "$(echo -e "${_lines}")"
|
||||||
|
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_OPEN}" "${_all}"
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_bookmark() {
|
||||||
|
local _lines="$(search "${@}")"
|
||||||
|
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__X:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
|
||||||
|
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
|
||||||
|
read -p"More than one URL found. Copy all ? [Y/N] : " -n1 _answer
|
||||||
|
[[ "${_answer,,}" != 'y' ]] && die "\nUse -X to force the first URL or refine your search"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
local _all=''
|
||||||
|
while read _nl; do
|
||||||
|
readLines <<< "${_nl}"
|
||||||
|
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
|
||||||
|
_all="$(awk -F'|' '{print $4}' <<< "${_nl}") ${_all}"
|
||||||
|
else
|
||||||
|
awk -F'|' '{print $4}' <<< "${_nl}" | "${_BM_CMD_COPY}"
|
||||||
|
awk -F'|' '{print $4}' <<< "${_nl}" | "${_BM_CMD_COPY}" -b
|
||||||
|
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__X:=0} -eq 1 ]] && exit 0
|
||||||
|
fi
|
||||||
|
done <<< "$(echo -e "${_lines}")"
|
||||||
|
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_COPY}" <<< "${_all}"
|
||||||
|
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_COPY}" -b <<< "${_all}"
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_bookmark() {
|
||||||
|
[[ -z "${@//[ ]/}" ]] && die "You MUST give an argument !"
|
||||||
|
local _lines="$(search "${@}")"
|
||||||
|
if [ "${_BM_DELETE_ALLOWED,,}" = 'true' ]; then
|
||||||
|
while read _nl; do readLines <<< "${_nl}"; done <<< "$(echo -e "${_lines}")"
|
||||||
|
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__D:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
|
||||||
|
if [ $(wc -l <<< "${_lines}") -ge 1 ]; then
|
||||||
|
read -p"You're about to delete entry(ies). Ready ? [Y/N] : " -n1 _answer
|
||||||
|
[[ "${_answer,,}" != 'y' ]] && die "\nUse -D to force the backup if not configured."
|
||||||
|
echo ''
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
backupBm
|
||||||
|
local _all=''
|
||||||
|
while read _nl; do
|
||||||
|
if [ "${_BM_DELETE_TO_FILE,,}" = 'true' -o ${__D:=0} -eq 1 ]; then
|
||||||
|
echo "${_nl}" >> "${_BM_DELETE_FILE}"
|
||||||
|
fi
|
||||||
|
[[ "${_BM_DELETE_PICTURE,,}" = 'true' || ${__p:=0} -eq 1 ]] && rm -f -- "${_BM_SCREENSHOT_DIRECTORY}/${_nl%%|*}.png"
|
||||||
|
IFS='|' read m d a u T t <<< "${_nl}"
|
||||||
|
sed -i -e '/^'"${m}.*${u//\//\\/}.*${t//\//\\/}"'$/d' "${_BM_BOOKMARK_FILE}"
|
||||||
|
done <<< "$(echo -e "${_lines}")"
|
||||||
|
else
|
||||||
|
die "You're not allowed to delete entries. Change the _BM_DELETE_ALLOWED to true !"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_bookmark() {
|
||||||
|
echo -e "You're about to open your '${_BM_BOOKMARK_FILE}' file with your \$EDITOR.\n\n/!\ Press any key to continue, or CTRL+C to abort. /!\ "
|
||||||
|
read -n1 toto
|
||||||
|
${EDITOR:=vi} "${_BM_BOOKMARK_FILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Diplay some statistics about the bookmarks
|
||||||
|
#
|
||||||
|
stats() {
|
||||||
|
echo "===== Configuration ====="
|
||||||
|
echo "Bookmark file : ${_BM_BOOKMARK_FILE}"
|
||||||
|
echo "Trash file : ${_BM_DELETE_FILE}$([[ ! -e "${_BM_DELETE_FILE}" ]] && echo " (but doesn't exist)")"
|
||||||
|
echo "Config file : ${_BM_CONFIG_FILE}$([[ ! -e "${_BM_CONFIG_FILE}" ]] && echo " (but doesn't exist)")"
|
||||||
|
echo "Screenshot directory : ${_BM_SCREENSHOT_DIRECTORY}"
|
||||||
|
echo "Backup file(s) : ${_BM_BOOKMARK_BACKUP_FILE}*"
|
||||||
|
for i in "${_BM_BOOKMARK_BACKUP_FILE}"*; do
|
||||||
|
echo " ${i}"
|
||||||
|
done
|
||||||
|
echo -e "\n===== Statistics ====="
|
||||||
|
readarray -t lines < "${_BM_BOOKMARK_FILE}"
|
||||||
|
echo "# of Bookmarks : ${#lines[@]}"
|
||||||
|
echo "# of Duplicate : $(awk -F'|' '$0 !~ /^[ ]*#/ {print $1}' "${_BM_BOOKMARK_FILE}" |sort |uniq -d |wc -l)"
|
||||||
|
local tags="$(awk -F'|' '$0 !~ /^[ ]*#/ {print $NF}' "${_BM_BOOKMARK_FILE}" | sed -e 's/\([[:space:]]*,[[:space:]]*\)/\n/g')"
|
||||||
|
|
||||||
|
echo "# of tags : $( sort -u <<< "${tags,,}" |wc -l)"
|
||||||
|
echo "Top 14 tags used :"
|
||||||
|
local nli=0;local pa=0
|
||||||
|
(
|
||||||
|
sort <<< "${tags,,}" | uniq -c | sort -nr | while read n t; do
|
||||||
|
(( nli++ ));[[ ${nli} -gt 7 ]] && nli=1 && echo ''
|
||||||
|
(( pa++ ));[[ ${pa} -gt 14 ]] && break
|
||||||
|
echo -n "${t// / }:${n} "
|
||||||
|
done
|
||||||
|
echo ''
|
||||||
|
) | column -t -c 17
|
||||||
|
echo "# of Pictures : $(ls -1 "${_BM_SCREENSHOT_DIRECTORY}"/*.png |wc -l) [# of files in ${_BM_SCREENSHOT_DIRECTORY}:$(ls -1 "${_BM_SCREENSHOT_DIRECTORY}"/* |wc -l)]"
|
||||||
|
echo "All Pictures size : $(du -sh "${_BM_SCREENSHOT_DIRECTORY}" | awk '{print $1}')"
|
||||||
|
local orphaned="$( cd "${_BM_SCREENSHOT_DIRECTORY}" && for i in *; do [[ -z "$(sed -e '/'"${i%%.*}"'/!d' "${_BM_BOOKMARK_FILE}")" ]] && ((orph++)); done; echo "${orph:=0}" )"
|
||||||
|
local nopics="$( while read i; do [[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${i%%|*}.png" ]] && ((nopics++)); done <<< ${lines[@]}; echo "${nopics:=0}" )"
|
||||||
|
echo "Bookmark Without Pic : ${nopics}"
|
||||||
|
echo "Orphaned pictures : ${orphaned}"
|
||||||
|
[[ ${__p:=0} -eq 1 ]] && { echo -e "\n===== Orphaned ====="; echo "List of orphaned pictures :"; cd "${_BM_SCREENSHOT_DIRECTORY}" && for i in *; do [[ -z "$(sed -e '/'"${i%%.*}"'/!d' "${_BM_BOOKMARK_FILE}")" ]] && echo " - ${_BM_SCREENSHOT_DIRECTORY}/${i} [Trashed URL should be: $(awk -F'|' 'BEGIN{l="Unknown"} ($1 ~ /'"${i%%.*}"'/) {l=$4;} END {print l}' "${_BM_DELETE_FILE}")]"; done; };
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Output tags.
|
||||||
|
#
|
||||||
|
|
||||||
|
list_tags() {
|
||||||
|
local tags="$(awk -F'|' '$0 !~ /^[ ]*#/ {print $NF}' "${_BM_BOOKMARK_FILE}" | sed -e 's/\([[:space:]]*,[[:space:]]*\)/\n/g')"
|
||||||
|
local nli=0
|
||||||
|
(
|
||||||
|
sort <<< "${tags,,}" | uniq -c | sort -nr | while read n t; do
|
||||||
|
(( nli++ ));[[ ${nli} -gt 7 ]] && nli=1 && echo ''
|
||||||
|
echo -n "${t// / }:${n} "
|
||||||
|
done
|
||||||
|
echo ''
|
||||||
|
) | column -t -c 17
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Stylesheet
|
||||||
|
#
|
||||||
|
|
||||||
|
style() {
|
||||||
|
cat <<EOF
|
||||||
|
<style type="text/css">
|
||||||
|
* {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
padding: 50px 0 5px 50px;
|
||||||
|
background: #1f1f1f url(${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png);
|
||||||
|
font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.bm {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
margin: 15px;
|
||||||
|
padding: 1px;
|
||||||
|
opacity: 1;
|
||||||
|
border: 15px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-transition: -webkit-box-shadow 600ms;
|
||||||
|
-moz-transition: -webkit-box-shadow 600ms;
|
||||||
|
-webkit-box-shadow: 0 0 1px 0 #555, 0 0 10px rgba(0,0,0,.5);
|
||||||
|
-moz-box-shadow: 0 0 1px 0 #555, 0 0 10px rgba(0,0,0,.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.bm:hover {
|
||||||
|
-webkit-box-shadow: 0 0 40px #1ab0ff
|
||||||
|
, 0 0 3px #06bdff
|
||||||
|
, 0 1px 1px #4ee2ff
|
||||||
|
, 0 1px 0 #fff;
|
||||||
|
-moz-box-shadow: 0 0 40px #1ab0ff
|
||||||
|
, 0 0 3px #06bdff
|
||||||
|
, 0 1px 1px #4ee2ff
|
||||||
|
, 0 1px 0 #fff;
|
||||||
|
}
|
||||||
|
.bm:hover img {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.bm img {
|
||||||
|
width: 200px;
|
||||||
|
height: 150px;
|
||||||
|
opacity: .5;
|
||||||
|
border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-transition: opacity 200ms;
|
||||||
|
-moz-transition: opacity 200ms;
|
||||||
|
}
|
||||||
|
.bm p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(0,0,0,.75);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: white;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
bottom: -50px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-webkit-transition: bottom 200ms ease-in;
|
||||||
|
}
|
||||||
|
.bm:hover p {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generate bookmark screenshots
|
||||||
|
#
|
||||||
|
|
||||||
|
screenshot_take() {
|
||||||
|
if [ "${_BM_SCREENSHOT_GET,,}" = 'true' -o ${__p:=0} -eq 1 ]; then
|
||||||
|
[[ ! -d "${_BM_SCREENSHOT_DIRECTORY}" ]] && { mkdir -p "${_BM_SCREENSHOT_DIRECTORY}" || die "Can't create thumbnail directory." ; }
|
||||||
|
[[ ! -z "${_BM_CMD_PRE_CAPTURE}" ]] && { local _pre="${_BM_CMD_PRE_CAPTURE//\{FILE\}/${1}}"; _pre="${_pre//\{URL\}/${2}}"; ${_pre} ; }
|
||||||
|
local _cmd="${_BM_CMD_CAPTURE//\{FILE\}/${1}}"; _cmd="${_cmd//\{URL\}/${2}}";
|
||||||
|
local _args="${_BM_CMD_CAPTURE_ARGS//\{FILE\}/${1}}"; _args="${_args//\{URL\}/${2}}";
|
||||||
|
eval ${_cmd} ${_args}
|
||||||
|
[[ ! -z "${_BM_CMD_POST_CAPTURE}" ]] && { local _post="${_BM_CMD_POST_CAPTURE//\{FILE\}/${1}}"; _post="${_post//\{URL\}/${2}}"; ${_post} ; }
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
picturize() {
|
||||||
|
if [ -z "${1:-}" ]; then
|
||||||
|
awk -F'|' '$0 !~ /^[ ]*#/ {print $1,$4;}' "${_BM_BOOKMARK_FILE}" | while read m u; do
|
||||||
|
[[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" || ${__F:=0} -eq 1 ]] && { screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" & }
|
||||||
|
[[ "${_BM_SCREENSHOT_WAIT,,}" = 'true' ]] && wait
|
||||||
|
done
|
||||||
|
else
|
||||||
|
local _lines="$(search "${@}")"
|
||||||
|
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
|
||||||
|
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
|
||||||
|
read -p"More than one URL found. Open all ? [Y/N] : " -n1 _answer
|
||||||
|
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
local _all=''
|
||||||
|
while read _nl; do
|
||||||
|
readLines <<< "${_nl}"
|
||||||
|
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
|
||||||
|
IFS='|' read m d a u b <<< "${_nl}"
|
||||||
|
screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" &
|
||||||
|
else
|
||||||
|
IFS='|' read m d a u b <<< "${_nl}"
|
||||||
|
screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" &
|
||||||
|
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
|
||||||
|
fi
|
||||||
|
done <<< "$(echo -e "${_lines}")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmark_generator() {
|
||||||
|
[[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png" && -f "/usr/share/bm/black-Linen.png" ]] && cp "/usr/share/bm/black-Linen.png" "${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png"
|
||||||
|
echo "<!DOCTYPE html><html><head><meta charset="UTF-8"><title>bm v${VERSION} : all your bookmarks</title>" > "${1:-${_BM_HTML_FILE}}"
|
||||||
|
style >> "${1:-${_BM_HTML_FILE}}"
|
||||||
|
echo "</head><body>" >> "${1:-${_BM_HTML_FILE}}"
|
||||||
|
# search "${2:-}" | while IFS='|' read m a u T t d; do
|
||||||
|
awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | while IFS='|' read m d a u T t; do
|
||||||
|
echo " <div class=bm>
|
||||||
|
<a href='${u}'><img src='${_BM_SCREENSHOT_DIRECTORY}/${m}.png' alt='${u}' /></a>
|
||||||
|
<p>${T}<br/><a href='${_BM_SCREENSHOT_DIRECTORY}/${m}.png'>View image</a></p>
|
||||||
|
</div>" >> "${1:-${_BM_HTML_FILE}}"
|
||||||
|
done
|
||||||
|
echo "</body></html>" >> "${1:-${_BM_HTML_FILE}}"
|
||||||
|
[[ ${__O:=0} -eq 1 ]] && "${_BM_CMD_OPEN}" "${1:-${_BM_HTML_FILE}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# die print message to stderr and exit
|
||||||
|
#
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo -e "${@}" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# checkBinaries check the script is able to run and give hints
|
||||||
|
#
|
||||||
|
|
||||||
|
checkBinaries() {
|
||||||
|
if [ ${#} -ne 0 ]; then
|
||||||
|
local cmdErr="${@}"
|
||||||
|
else
|
||||||
|
local cmdErr="sed awk date iconv cat curl ${_BM_CMD_OPEN} ${_BM_CMD_MD5} column"
|
||||||
|
fi
|
||||||
|
set ${cmdErr}
|
||||||
|
while [ ${#} -ne 0 ]; do
|
||||||
|
if ! command -v "${1:-}" &>/dev/null; then
|
||||||
|
echo "command not found: ${1}" >&2
|
||||||
|
local rc=1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
[[ ${rc:=0} -ne 0 ]] && die "At least one command is missing. Please install it before using bm."
|
||||||
|
# Checking sed
|
||||||
|
local rc=1
|
||||||
|
[[ ! -f "/tmp/sedtest.$$" ]] && \
|
||||||
|
echo -n 'toto' > "/tmp/sedtest.$$" && \
|
||||||
|
sed -i.bak -e 's;^toto$;tata;' "/tmp/sedtest.$$" && \
|
||||||
|
[[ -f "/tmp/sedtest.$$.bak" ]] && \
|
||||||
|
grep -q "tata" "/tmp/sedtest.$$" && \
|
||||||
|
rc=0 && \
|
||||||
|
rm -f "/tmp/sedtest.$$" "/tmp/sedtest.$$.bak"
|
||||||
|
[[ ${rc} -ne 0 ]] && die "sed seems to not handle -i argument properly, please check"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# defineColors generate the variables to use to colorize the output
|
||||||
|
#
|
||||||
|
|
||||||
|
defineColors() {
|
||||||
|
export BLACK="\e[30m"; export BLACK_LIGHT="\e[90m"; export GRAY_DARK="${BLACK_LIGHT}"
|
||||||
|
export RED="\e[31m"; export RED_LIGHT="\e[91m"
|
||||||
|
export GREEN="\e[32m"; export GREEN_LIGHT="\e[92m"
|
||||||
|
export YELLOW="\e[33m"; export YELLOW_LIGHT="\e[93m"
|
||||||
|
export BLUE="\e[34m"; export BLUE_LIGHT="\e[94m"
|
||||||
|
export MAGENTA="\e[35m"; export MAGENTA_LIGHT="\e[95m"
|
||||||
|
export CYAN="\e[36m"; export CYAN_LIGHT="\e[96m"
|
||||||
|
export GRAY="\e[37m"; export GRAY_LIGHT="\e[97m"; export WHITE="${GRAY_LIGHT}"
|
||||||
|
|
||||||
|
export RESET="\e[0m"
|
||||||
|
export BOLD="\e[1m"; export BOLD_RESET="\e[21m"
|
||||||
|
export DIM="\e[2m"; export DIM_RESET="\e[22m"
|
||||||
|
export UNDERLINE="\e[4m"; export UNDERLINE_RESET="\e[24m"
|
||||||
|
export INVERT="\e[7m"; export INVERT_RESET="\e[27m"
|
||||||
|
if [ ! -z "${1:-}" ]; then
|
||||||
|
echo "Use the following colors to fit your needs :"
|
||||||
|
(
|
||||||
|
echo -e "${RESET}${BLACK}BLACK${RESET} - ${BLACK_LIGHT}BLACK_LIGHT${RESET} - ${BOLD}${BLACK}BOLD BLACK${RESET} - ${DIM}${BLACK}DIM BLACK${RESET}"
|
||||||
|
echo -e "${RESET}${RED}RED${RESET} - ${RED_LIGHT}RED_LIGHT${RESET} - ${BOLD}${RED}BOLD RED${RESET} - ${DIM}${RED}DIM RED${RESET}"
|
||||||
|
echo -e "${RESET}${GREEN}GREEN${RESET} - ${GREEN_LIGHT}GREEN_LIGHT${RESET} - ${BOLD}${GREEN}BOLD GREEN${RESET} - ${DIM}${GREEN}DIM GREEN${RESET}"
|
||||||
|
echo -e "${RESET}${YELLOW}YELLOW${RESET} - ${YELLOW_LIGHT}YELLOW_LIGHT${RESET} - ${BOLD}${YELLOW}BOLD YELLOW${RESET} - ${DIM}${YELLOW}DIM YELLOW${RESET}"
|
||||||
|
echo -e "${RESET}${BLUE}BLUE${RESET} - ${BLUE_LIGHT}BLUE_LIGHT${RESET} - ${BOLD}${BLUE}BOLD BLUE${RESET} - ${DIM}${BLUE}DIM BLUE${RESET}"
|
||||||
|
echo -e "${RESET}${MAGENTA}MAGENTA${RESET} - ${MAGENTA_LIGHT}MAGENTA_LIGHT${RESET} - ${BOLD}${MAGENTA}BOLD MAGENTA${RESET} - ${DIM}${MAGENTA}DIM MAGENTA${RESET}"
|
||||||
|
echo -e "${RESET}${CYAN}CYAN${RESET} - ${CYAN_LIGHT}CYAN_LIGHT${RESET} - ${BOLD}${CYAN}BOLD CYAN${RESET} - ${DIM}${CYAN}DIM CYAN${RESET}"
|
||||||
|
echo -e "${RESET}${GRAY}GRAY${RESET} - ${GRAY_LIGHT}GRAY_LIGHT${RESET} - ${BOLD}${GRAY}BOLD GRAY${RESET} - ${DIM}${GRAY}DIM GRAY${RESET}"
|
||||||
|
echo -e "${RESET}${GRAY_DARK}GRAY_DARK${RESET} - ${WHITE}WHITE${RESET}"
|
||||||
|
) | column -t
|
||||||
|
echo -e "You could also use the ${UNDERLINE}UNDERLINE, the ${INVERT}INVERT and the ${RESET}RESET variable. INVERT_RESET and UNDERLINE_RESET also available"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# MAIN
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
##################### TRANSITIONNAL ############################
|
||||||
|
_regex='^(help|version|tags|ls|list|search|open|add|view|stats|statistics|clear|colors)$'
|
||||||
|
if [[ ${1:-} =~ ${_regex} ]]; then
|
||||||
|
case "$1" in
|
||||||
|
tags) __L=1 ;;
|
||||||
|
ls|list) __l=1 ;;
|
||||||
|
search) __s=1; __search="${@:2}" ;;
|
||||||
|
open) __o=1; __open="${@:2}";;
|
||||||
|
add) __a=1; __url="${2:-}"; __T="${3:-}"; __t="${4:-}"; __A="${5:-}";;
|
||||||
|
view) __r=1; __search="${@:2}" ;;
|
||||||
|
stats|statistics) __S=1 ;;
|
||||||
|
clear) die "This now a deprecated feature. Use -d instead." ;;
|
||||||
|
colors) __C=1;;
|
||||||
|
help) usage 1;exit 0;;
|
||||||
|
version) echo -n "${0} v${VERSION}";exit 0;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
# -c config file
|
||||||
|
# -C colors print ==> exit
|
||||||
|
# -b bookmark file
|
||||||
|
# -d delete
|
||||||
|
# -D deletefile if set override _BM_DELETE_TO_FILE to true
|
||||||
|
# -w working dir
|
||||||
|
# -a add url =/= -l, -s, -S, -O, -o, -x, -X, -g, -C
|
||||||
|
# -A Accelerator
|
||||||
|
# -T title
|
||||||
|
# -t tags
|
||||||
|
# -s search
|
||||||
|
# -S stats
|
||||||
|
# -l list all url
|
||||||
|
# -o open
|
||||||
|
# -O open first
|
||||||
|
# -x copy to clipboard
|
||||||
|
# -X copy first URL
|
||||||
|
# -h help
|
||||||
|
# -H more help
|
||||||
|
# -v version
|
||||||
|
# -V more version ?
|
||||||
|
# -g generate html file
|
||||||
|
# -G use this file
|
||||||
|
# -g -g generate 1 file per tags
|
||||||
|
# used : AaBbCcDdE-F-GgHh-i----Ll--NnOoPp-q-rSsTt--Vv-wXxY--z----------
|
||||||
|
# available : ---------e-f----I-JjKk--Mm------Q-R-----Uu--W----yZ-0123456789
|
||||||
|
while getopts ":a:A:b:c:d:G:o:P:q:r:s:t:T:w:x:BCDEFghHilLnNOpPSvVXYz" option; do
|
||||||
|
case ${option} in
|
||||||
|
a) __a=1; __url="${OPTARG}";; # Add url to bookmark
|
||||||
|
A) __A="${OPTARG}";; # AcceleratoR
|
||||||
|
b) __b="${OPTARG}";; # Bookmark file
|
||||||
|
B) __B=1;; # Don't load the default config file
|
||||||
|
c) __c="${OPTARG}";; # Config file
|
||||||
|
C) __C=1;; # Print color table
|
||||||
|
d) __d=1; __del="${OPTARG}";; # Config file
|
||||||
|
D) __D=1;; # Print color table
|
||||||
|
E) __E=1;; # Open the bm.bm file with the $EDITOR
|
||||||
|
F) __F=1;; # Force the add or delete or picture
|
||||||
|
g) (( __g++ ));; # generate html file(s)
|
||||||
|
G) __G=1; __file="${OPTARG}";; # Generate this file (only for g=1)
|
||||||
|
h|H) usage; exit 0;; # Help
|
||||||
|
i) __i=1;; # Ignore case when searching
|
||||||
|
l) __l=1;; # List all bookmarks
|
||||||
|
L) __L=1;; # List all tags
|
||||||
|
n) __n=1;; # sort by date
|
||||||
|
N) __N=1;; # sort by date (reverse)
|
||||||
|
o) __o=1; __open="${OPTARG}";; # Open
|
||||||
|
O) __O=1;; # Open First
|
||||||
|
p) __p=1;; # Take a screenshot
|
||||||
|
P) __P=1; __search="${OPTARG}";; # Take all screenshot
|
||||||
|
q) __q=1; __search="${OPTARG}";; # Search
|
||||||
|
r) __r=1; __search="${OPTARG}";; # Search
|
||||||
|
s) __s=1; __search="${OPTARG}";; # Search
|
||||||
|
S) __S=1;; # Print Statistics
|
||||||
|
t) __t="${OPTARG}";; # tags for a URL
|
||||||
|
T) __T="${OPTARG}";; # Title for a URL
|
||||||
|
v|V) echo -n "${0} v${VERSION}"; [[ "${option}" = 'V' ]] && echo -n " [commit: ${RELEASE}]"; echo ''; exit 0;;
|
||||||
|
w) __w="${OPTARG}";; # Working directory
|
||||||
|
x) __x=1; __copy="${OPTARG}";; # Copy
|
||||||
|
X) __X=1;; # Copy First
|
||||||
|
Y) __Y=1;; # Open/copy All
|
||||||
|
z) __z=1;; # Alternative print listing
|
||||||
|
:) echo "Missing argument for '-${OPTARG}'" >&2 ; exit 1 ;;
|
||||||
|
?) echo "Argument unknown '-${OPTARG}'" >&2 ; exit 1 ;; # usage;;
|
||||||
|
*) echo "Argument unknown '-${option}'" >&2 ; exit 1 ;; # usage;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parsing args is done, Starting to work, so check if binaries are here
|
||||||
|
checkBinaries
|
||||||
|
|
||||||
|
# defineColors MUST be called before the config() or _BM_PRINT_LINE will be in trouble.
|
||||||
|
defineColors
|
||||||
|
# Before loading config, checking if working dir exist...
|
||||||
|
if [ ! -z "${__w:=}" -a ! -d "${__w}" ]; then
|
||||||
|
die "Working directory '${__w}' doesn't exist !" >&2
|
||||||
|
fi
|
||||||
|
# Loading config
|
||||||
|
config "${__c}"
|
||||||
|
[[ ! -z "${__b:=}" ]] && export _BM_BOOKMARK_FILE="${__b}"
|
||||||
|
|
||||||
|
# Following config, we maybe not have to check the capture tool
|
||||||
|
[[ ${_BM_CMD_CAPTURE_CHECK} ]] && checkBinaries "${_BM_CMD_CAPTURE}"
|
||||||
|
|
||||||
|
# If no bookmark file exist, create one if allowed
|
||||||
|
if [ ! -f "${_BM_BOOKMARK_FILE}" -a "${_BM_CREATE_BOOKMARK_FILE,,}" = 'true' ]; then
|
||||||
|
# Remember fields : 1=md5, 2=date, 2=accel, 3=url, 4=title, 5=tags
|
||||||
|
echo "eef521de8df447ad392dbace16cf2edc|$(date '+%FT%TZ')|:bm|https://github.com/The-Repo-Club/repomenu-extra/|Download link for repomenu-extra|default,shell,scripts" >> "${_BM_BOOKMARK_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Starting to work with args. If none probably list...
|
||||||
|
[[ ${#} -eq 0 && "${_BM_NO_ARGS_FORCE_HELP,,}" = 'true' ]] && { usage ; exit; }
|
||||||
|
|
||||||
|
# Only one action at a time
|
||||||
|
if [ $(( ${__a:=0} + ${__C:=0} + ${__d:=0} + ${__E:=0} + ${__g:=0} + ${__l:=0} + ${__L:=0} + ${__o:=0} + ${__P:=0} + ${__q:=0} + ${__r:=0} + ${__s:=0} + ${__S:=0} + ${__x:=0} )) -gt 1 ]; then
|
||||||
|
echo "You have to choose between -a, -C, -d, -E, -g, -l, -L, -o, -P, -q, -r, -s, -S, -x" >&2
|
||||||
|
echo "Use -h for help" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ $(( ${__O:=0} + ${__Y:=0} + ${__X:=0} )) -gt 1 ]; then
|
||||||
|
echo "You have to choose between -O, -X, -Y" >&2
|
||||||
|
echo "Use -h for help" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Executing actions
|
||||||
|
[[ ${__a} -eq 1 ]] && { saveUrl ; exit ; }
|
||||||
|
[[ ${__C} -eq 1 ]] && { defineColors 1 ; exit ; }
|
||||||
|
[[ ${__d} -eq 1 ]] && { delete_bookmark "${__del}"; exit ; }
|
||||||
|
[[ ${__g} -ge 1 ]] && { bookmark_generator "${__file:=}" ''; exit ; }
|
||||||
|
[[ ${__l} -eq 1 ]] && { search ; exit; }
|
||||||
|
[[ ${__L} -eq 1 ]] && { list_tags ; exit; }
|
||||||
|
[[ ${__o} -eq 1 ]] && { open_bookmark "${__open}" ; exit; }
|
||||||
|
[[ ${__P} -eq 1 ]] && { picturize "${__search}" ; exit; }
|
||||||
|
[[ ${__r} -eq 1 ]] && { recorded_picture "${__search}" ; exit; }
|
||||||
|
[[ ${__q} -eq 1 ]] && { download_title "${__search}" ; exit; }
|
||||||
|
[[ ${__s} -eq 1 ]] && { search_bookmarks "${__search}" ; exit; }
|
||||||
|
[[ ${__S} -eq 1 ]] && { stats ; exit; }
|
||||||
|
[[ ${__x} -eq 1 ]] && { copy_bookmark "${__copy}" ; exit; }
|
||||||
|
[[ ${__E} -eq 1 ]] && { edit_bookmark ; exit; }
|
||||||
|
search
|
||||||
|
|
||||||
|
# $Format:%cn @ %cD$ : $Id$
|
37
.local/bin/rofi/bm_add
Executable file
37
.local/bin/rofi/bm_add
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# bm_add
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Fri 09 December 2022, 06:43:52 AM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Fri 09 December 2022, 08:03:21 AM [GMT]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# bm
|
||||||
|
#
|
||||||
|
|
||||||
|
result() {
|
||||||
|
echo -n | rofi -dmenu -p "${1:-Add a bookmark:...}" -mesg "+ Add a Bookmark"
|
||||||
|
}
|
||||||
|
|
||||||
|
url="$(result "URL:")"
|
||||||
|
|
||||||
|
if [ -z "$url" ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
title="$(result "Title:")"
|
||||||
|
tags="$(result "Tags (comma delimited):")"
|
||||||
|
|
||||||
|
bm -w "$HOME/.config/rofi/bookmarks/" -a "$url" -T "$title" -t "$tags"
|
58
.local/bin/rofi/bm_remove
Executable file
58
.local/bin/rofi/bm_remove
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# bm_remove
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Fri 09 December 2022, 06:43:41 AM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Fri 14 July 2023, 11:04:29 PM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# bm
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
bmFile="$HOME/.config/rofi/bookmarks/bm.bm"
|
||||||
|
|
||||||
|
if [[ ! -f $bmFile ]]; then
|
||||||
|
printf "%s\n" "No current bookmark file found.";
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -A bmarray;
|
||||||
|
|
||||||
|
while IFS=\| read -r md5 date accel url title tags;
|
||||||
|
do
|
||||||
|
bookmark="$title-$url-$tags";
|
||||||
|
bmarray["$bookmark"]="$url";
|
||||||
|
done < "$bmFile"
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
while IFS=\| read -r md5 date accel url title tags;
|
||||||
|
do
|
||||||
|
bookmark="$title-$url-$tags";
|
||||||
|
printf "%s\n" "$bookmark";
|
||||||
|
done < "$bmFile"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
choice=$(load | rofi -dmenu i -p "${1:-Remove a bookmark:...}" -mesg "- Remove a Bookmark")
|
||||||
|
|
||||||
|
LOOPSETTING="true"
|
||||||
|
while [ -n "$LOOPSETTING" ]; do
|
||||||
|
[ -n "$choice" ] || exit
|
||||||
|
unset LOOPSETTING
|
||||||
|
case "$choice" in
|
||||||
|
*) bm -w "$HOME/.config/rofi/bookmarks/" -d "${bmarray[$choice]}" -D;;
|
||||||
|
esac
|
||||||
|
done
|
54
.local/bin/rofi/bm_viewer
Executable file
54
.local/bin/rofi/bm_viewer
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# bm_viewer
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Fri 09 December 2022, 06:43:30 AM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Fri 14 July 2023, 11:04:22 PM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# bm
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
bmFile="$HOME/.config/rofi/bookmarks/bm.bm"
|
||||||
|
|
||||||
|
if [[ ! -f $bmFile ]]; then
|
||||||
|
printf "%s\n" "No current bookmark file found.";
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -A bmarray;
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
printf "Add Bookmark\nRemove Bookmark\n"
|
||||||
|
while IFS=\| read -r md5 date accel url title tags;
|
||||||
|
do
|
||||||
|
bookmark="$title-$url-$tags";
|
||||||
|
printf "%s\n" "$title";
|
||||||
|
done < "$bmFile"
|
||||||
|
}
|
||||||
|
|
||||||
|
choice=$(load | rofi -dmenu -p "${1:-Select a bookmark:...}" -mesg "+ Add Bookmark - Remove Bookmark Select Bookmarks")
|
||||||
|
|
||||||
|
LOOPSETTING="true"
|
||||||
|
while [ -n "$LOOPSETTING" ]; do
|
||||||
|
[ -n "$choice" ] || exit
|
||||||
|
unset LOOPSETTING
|
||||||
|
case "$choice" in
|
||||||
|
Add*) bm_add ;;
|
||||||
|
Remove*) bm_remove ;;
|
||||||
|
*) bm -w "$HOME/.config/rofi/bookmarks/" -o "${bmarray[$choice]}";;
|
||||||
|
esac
|
||||||
|
done
|
25
.local/bin/rofi/clipmenu_run
Executable file
25
.local/bin/rofi/clipmenu_run
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# clipmenu_run
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 07 December 2022, 11:01:52 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Sun 06 August 2023, 11:39:26 AM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# <None>
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
clipmenu -p ClipMenu -mesg "Copy from clipboard."
|
27
.local/bin/rofi/menu
Executable file
27
.local/bin/rofi/menu
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# menu
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 10 March 2021, 12:34:47 PM [GMT+1]
|
||||||
|
#Modified:
|
||||||
|
# Thu 08 December 2022, 07:37:35 AM [GMT]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# rofi
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
programs=$(compgen -c | sort -u | tail -n +9)
|
||||||
|
cmd=$(echo -e "$programs" | rofi -dmenu -p "Programs" -mesg "Select a program.")
|
||||||
|
exec $cmd
|
64
.local/bin/rofi/music
Executable file
64
.local/bin/rofi/music
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# -*-coding:utf-8 -*-
|
||||||
|
# Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File :
|
||||||
|
# music
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
# Created:
|
||||||
|
# Wed 10 March 2021, 12:34:47 PM [GMT]
|
||||||
|
# Modified:
|
||||||
|
# Sat 05 August 2023, 11:57:57 PM [GMT+1]
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
MUSICPLAYER="mplayer"
|
||||||
|
CURRENT_SONG=$(lsof -c "$MUSICPLAYER" | grep -F ".mp3" | awk -F"/" '{ print $NF; }' | cut -d'.' -f1)
|
||||||
|
PLAYING_MSG="Currently Playing :: ${CURRENT_SONG}"
|
||||||
|
MUSICDIR=$HOME/Music
|
||||||
|
|
||||||
|
for Song in "$MUSICDIR/"*; do
|
||||||
|
if [ -f "$Song" ]; then
|
||||||
|
Name=${Song##*/}
|
||||||
|
case $Name in
|
||||||
|
*.mp3 | *.flac | *.wav | .ogg)
|
||||||
|
options+=${Song##*/}$'\n'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
CHOICE=$(rofi -dmenu -no-custom -p "Muisc Player" -kb-custom-1 "Alt+Return" -mesg "${PLAYING_MSG}" "$@" <<ENDOPTS
|
||||||
|
${options::-1}
|
||||||
|
ENDOPTS
|
||||||
|
)
|
||||||
|
|
||||||
|
ret=$?
|
||||||
|
|
||||||
|
if [[ $ret -eq 0 ]]; then
|
||||||
|
case $CHOICE in
|
||||||
|
*.mp3 | *.flac | *.wav | *.ogg)
|
||||||
|
if pgrep -f "$MUSICPLAYER" >/dev/null; then
|
||||||
|
killall -9 "$MUSICPLAYER"
|
||||||
|
fi
|
||||||
|
if [[ $MUSICPLAYER == "mplayer" ]]; then
|
||||||
|
$MUSICPLAYER -ao alsa "$MUSICDIR/$CHOICE" >> /dev/null &
|
||||||
|
else
|
||||||
|
$MUSICPLAYER "$MUSICDIR/$CHOICE" &
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Program terminated." && exit
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
elif [[ $ret -gt 1 ]]; then
|
||||||
|
if [[ $ret -eq 10 ]]; then
|
||||||
|
killall -9 "$MUSICPLAYER"
|
||||||
|
fi
|
||||||
|
fi
|
897
.local/bin/rofi/passmenu
Executable file
897
.local/bin/rofi/passmenu
Executable file
@ -0,0 +1,897 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# passmenu
|
||||||
|
# (c) 2022 Wayne Wesley <wayne6324@gmail.com>
|
||||||
|
basecommand="$0"
|
||||||
|
|
||||||
|
# set default settings
|
||||||
|
_rofi () {
|
||||||
|
rofi -no-auto-select -i "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
_pwgen () {
|
||||||
|
pwgen -y "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
_image_viewer () {
|
||||||
|
feh -
|
||||||
|
}
|
||||||
|
|
||||||
|
_clip_in_primary() {
|
||||||
|
xclip
|
||||||
|
}
|
||||||
|
|
||||||
|
_clip_in_clipboard() {
|
||||||
|
xclip -selection clipboard
|
||||||
|
}
|
||||||
|
|
||||||
|
_clip_out_primary() {
|
||||||
|
xclip -o
|
||||||
|
}
|
||||||
|
|
||||||
|
_clip_out_clipboard() {
|
||||||
|
xclip --selection clipboard -o
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
config_dir=${XDG_CONFIG_HOME:-$HOME/.config}
|
||||||
|
cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}
|
||||||
|
|
||||||
|
# We expect to find these fields in pass(1)'s output
|
||||||
|
URL_field='url'
|
||||||
|
USERNAME_field='user'
|
||||||
|
AUTOTYPE_field='autotype'
|
||||||
|
OTPmethod_field='otp_method'
|
||||||
|
|
||||||
|
default_autotype="user :tab pass"
|
||||||
|
delay=2
|
||||||
|
wait=0.2
|
||||||
|
xdotool_delay=12
|
||||||
|
default_do='menu' # menu, copyPass, typeUser, typePass, copyUser, copyUrl, viewEntry, typeMenu, actionMenu, copyMenu, openUrl
|
||||||
|
auto_enter='false'
|
||||||
|
notify='false'
|
||||||
|
help_color=""
|
||||||
|
clip=primary
|
||||||
|
clip_clear=45
|
||||||
|
default_user="${ROFI_PASS_DEFAULT_USER-$(whoami)}"
|
||||||
|
default_user2=john_doe
|
||||||
|
password_length=12
|
||||||
|
fix_layout=false
|
||||||
|
|
||||||
|
# default shortcuts
|
||||||
|
autotype="Alt+1"
|
||||||
|
type_user="Alt+2"
|
||||||
|
type_pass="Alt+3"
|
||||||
|
open_url="Alt+4"
|
||||||
|
copy_name="Alt+u"
|
||||||
|
copy_url="Alt+l"
|
||||||
|
copy_pass="Alt+p"
|
||||||
|
show="Alt+o"
|
||||||
|
copy_menu="Alt+c"
|
||||||
|
action_menu="Alt+a"
|
||||||
|
type_menu="Alt+t"
|
||||||
|
help="Alt+h"
|
||||||
|
switch="Alt+x"
|
||||||
|
insert_pass="Alt+n"
|
||||||
|
qrcode="Alt+q"
|
||||||
|
previous_root="Shift+Left"
|
||||||
|
next_root="Shift+Right"
|
||||||
|
|
||||||
|
# Safe permissions
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
has_qrencode() {
|
||||||
|
command -v qrencode >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
listgpg () {
|
||||||
|
mapfile -d '' pw_list < <(find -L . -name '*.gpg' -print0)
|
||||||
|
pw_list=("${pw_list[@]#./}")
|
||||||
|
printf '%s\n' "${pw_list[@]}" | sort -n
|
||||||
|
}
|
||||||
|
|
||||||
|
# get all password files and output as newline-delimited text
|
||||||
|
list_passwords() {
|
||||||
|
cd "${root}" || exit
|
||||||
|
mapfile -t pw_list < <(listgpg)
|
||||||
|
printf '%s\n' "${pw_list[@]%.gpg}" | sort -n
|
||||||
|
}
|
||||||
|
|
||||||
|
doClip () {
|
||||||
|
case "$clip" in
|
||||||
|
"primary") _clip_in_primary ;;
|
||||||
|
"clipboard") _clip_in_clipboard ;;
|
||||||
|
"both") _clip_in_primary; _clip_out_primary | _clip_in_clipboard;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
checkIfPass () {
|
||||||
|
printf '%s\n' "${root}: $selected_password" >| "$cache_dir/passmenu/last_used"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
autopass () {
|
||||||
|
x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
||||||
|
xset r off
|
||||||
|
|
||||||
|
rm -f "$cache_dir/passmenu/last_used"
|
||||||
|
printf '%s\n' "${root}: $selected_password" > "$cache_dir/passmenu/last_used"
|
||||||
|
for word in ${stuff["$AUTOTYPE_field"]}; do
|
||||||
|
case "$word" in
|
||||||
|
":tab") xdotool key Tab;;
|
||||||
|
":space") xdotool key space;;
|
||||||
|
":delay") sleep "${delay}";;
|
||||||
|
":enter") xdotool key Return;;
|
||||||
|
":otp") printf '%s' "$(generateOTP)" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
|
||||||
|
"pass") printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
|
||||||
|
"path") printf '%s' "${selected_password}" | rev | cut -d'/' -f1 | rev | xdotool type --clearmodifiers --file -;;
|
||||||
|
*) printf '%s' "${stuff[${word}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${auto_enter} == "true" ]]; then
|
||||||
|
xdotool key Return
|
||||||
|
fi
|
||||||
|
|
||||||
|
xset r "$x_repeat_enabled"
|
||||||
|
unset x_repeat_enabled
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
generateQrCode() {
|
||||||
|
has_qrencode
|
||||||
|
|
||||||
|
if [[ $? -eq "1" ]]; then
|
||||||
|
printf '%s\n' "qrencode not found" | _rofi -dmenu
|
||||||
|
exit_code=$?
|
||||||
|
if [[ $exit_code -eq "1" ]]; then
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
"${basecommand}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
checkIfPass
|
||||||
|
pass "$selected_password" | head -n 1 | qrencode -d 300 -v 8 -l H -o - | _image_viewer
|
||||||
|
if [[ $? -eq "1" ]]; then
|
||||||
|
printf '%s\n' "" | _rofi -dmenu -mesg "Image viewer not defined or cannot read from pipe"
|
||||||
|
exit_value=$?
|
||||||
|
if [[ $exit_value -eq "1" ]]; then
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
"${basecommand}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
openURL () {
|
||||||
|
checkIfPass
|
||||||
|
$BROWSER "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${URL_field}: " | gawk '{sub(/:/,"")}{print $2}1' | head -1)"; exit;
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
typeUser () {
|
||||||
|
checkIfPass
|
||||||
|
|
||||||
|
x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
||||||
|
xset r off
|
||||||
|
|
||||||
|
printf '%s' "${stuff[${USERNAME_field}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
|
||||||
|
|
||||||
|
xset r "$x_repeat_enabled"
|
||||||
|
unset x_repeat_enabled
|
||||||
|
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
typePass () {
|
||||||
|
checkIfPass
|
||||||
|
|
||||||
|
x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
||||||
|
xset r off
|
||||||
|
|
||||||
|
printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
|
||||||
|
|
||||||
|
if [[ $notify == "true" ]]; then
|
||||||
|
if [[ "${stuff[notify]}" == "false" ]]; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
notify-send "passmenu" "finished typing password";
|
||||||
|
fi
|
||||||
|
elif [[ $notify == "false" ]]; then
|
||||||
|
if [[ "${stuff[notify]}" == "true" ]]; then
|
||||||
|
notify-send "passmenu" "finished typing password";
|
||||||
|
else
|
||||||
|
:
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
xset r "$x_repeat_enabled"
|
||||||
|
unset x_repeat_enabled
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
typeField () {
|
||||||
|
checkIfPass
|
||||||
|
local to_type
|
||||||
|
|
||||||
|
x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
||||||
|
xset r off
|
||||||
|
|
||||||
|
case $typefield in
|
||||||
|
"OTP") to_type="$(generateOTP)" ;;
|
||||||
|
*) to_type="${stuff[${typefield}]}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf '%s' "$to_type" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
|
||||||
|
|
||||||
|
xset r "$x_repeat_enabled"
|
||||||
|
unset x_repeat_enabled
|
||||||
|
unset to_type
|
||||||
|
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
generateOTP () {
|
||||||
|
checkIfPass
|
||||||
|
|
||||||
|
# First, we check if there is a non-conventional OTP command in the pass file
|
||||||
|
if PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep -q "${OTPmethod_field}: "; then
|
||||||
|
# We execute the commands after otp_method: AS-IS
|
||||||
|
bash -c "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${OTPmethod_field}: " | cut -d' ' -f2-)"
|
||||||
|
else
|
||||||
|
# If there is no method defined, fallback to pass-otp
|
||||||
|
PASSWORD_STORE_DIR="${root}" pass otp "$selected_password"
|
||||||
|
fi
|
||||||
|
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
copyUser () {
|
||||||
|
checkIfPass
|
||||||
|
printf '%s' "${stuff[${USERNAME_field}]}" | doClip
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
copyField () {
|
||||||
|
checkIfPass
|
||||||
|
printf '%s' "${stuff[${copyfield}]}" | doClip
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
copyURL () {
|
||||||
|
checkIfPass
|
||||||
|
printf '%s' "${stuff[${URL_field}]}" | doClip
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
copyPass () {
|
||||||
|
checkIfPass
|
||||||
|
printf '%s' "$password" | doClip
|
||||||
|
if [[ $notify == "true" ]]; then
|
||||||
|
notify-send "passmenu" "Copied Password\\nClearing in $clip_clear seconds"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $notify == "true" ]]; then
|
||||||
|
(sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard | notify-send "passmenu" "Clipboard cleared") &
|
||||||
|
elif [[ $notify == "false" ]]; then
|
||||||
|
(sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard) &
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
viewEntry () {
|
||||||
|
checkIfPass
|
||||||
|
showEntry "${selected_password}"
|
||||||
|
}
|
||||||
|
|
||||||
|
generatePass () {
|
||||||
|
askmenu_content=(
|
||||||
|
"Yes"
|
||||||
|
"No"
|
||||||
|
)
|
||||||
|
|
||||||
|
askGenMenu=$(printf '%s\n' "${askmenu_content[@]}" | _rofi -dmenu -p "Generate new Password for ${selected_password}? > ")
|
||||||
|
askgen_exit=$?
|
||||||
|
|
||||||
|
if [[ $askgen_exit -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
if [[ $askGenMenu == "Yes" ]]; then
|
||||||
|
true
|
||||||
|
elif [[ $askGenMenu == "No" ]]; then
|
||||||
|
actionMenu
|
||||||
|
fi
|
||||||
|
|
||||||
|
checkIfPass
|
||||||
|
|
||||||
|
symbols_content=(
|
||||||
|
"0 Cancel"
|
||||||
|
"1 Yes"
|
||||||
|
"2 No"
|
||||||
|
)
|
||||||
|
|
||||||
|
symbols=$(printf '%s\n' "${symbols_content[@]}" | _rofi -dmenu -p "Use Symbols? > ")
|
||||||
|
symbols_val=$?
|
||||||
|
|
||||||
|
if [[ $symbols_val -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
if [[ $symbols == "0 Cancel" ]]; then
|
||||||
|
mainMenu;
|
||||||
|
elif [[ $symbols == "1 Yes" ]]; then
|
||||||
|
symbols="";
|
||||||
|
elif [[ $symbols == "2 No" ]]; then
|
||||||
|
symbols="-n";
|
||||||
|
fi
|
||||||
|
|
||||||
|
HELP="<span color='$help_color'>Enter Number or hit Enter to use default length</span>"
|
||||||
|
length=$(printf '%s' "" | _rofi -dmenu -mesg "${HELP}" -p "Password length? (Default: ${password_length}) > ")
|
||||||
|
length_exit=$?
|
||||||
|
|
||||||
|
if [[ $length_exit -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
if [[ $length == "" ]]; then
|
||||||
|
PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${password_length}" > /dev/null;
|
||||||
|
else
|
||||||
|
PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${length}" > /dev/null;
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# main Menu
|
||||||
|
mainMenu () {
|
||||||
|
if [[ $1 == "--bmarks" ]]; then
|
||||||
|
selected_password="$(list_passwords 2>/dev/null \
|
||||||
|
| _rofi -mesg "Bookmarks Mode. ${switch} to switch" \
|
||||||
|
-dmenu \
|
||||||
|
-kb-custom-10 "${switch}" \
|
||||||
|
-select "$entry" \
|
||||||
|
-p "passmenu > ")"
|
||||||
|
|
||||||
|
rofi_exit=$?
|
||||||
|
|
||||||
|
if [[ $rofi_exit -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
elif [[ $rofi_exit -eq 19 ]]; then
|
||||||
|
${basecommand}
|
||||||
|
elif [[ $rofi_exit -eq 0 ]]; then
|
||||||
|
openURL
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unset selected_password
|
||||||
|
|
||||||
|
args=( -dmenu
|
||||||
|
-kb-custom-1 "${autotype}"
|
||||||
|
-kb-custom-2 "${type_user}"
|
||||||
|
-kb-custom-3 "${type_pass}"
|
||||||
|
-kb-custom-4 "${open_url}"
|
||||||
|
-kb-custom-5 "${copy_name}"
|
||||||
|
-kb-custom-6 "${copy_pass}"
|
||||||
|
-kb-custom-7 "${show}"
|
||||||
|
-kb-custom-8 "${copy_url}"
|
||||||
|
-kb-custom-9 "${type_menu}"
|
||||||
|
-kb-custom-10 "${previous_root}"
|
||||||
|
-kb-custom-11 "${next_root}"
|
||||||
|
-kb-custom-14 "${action_menu}"
|
||||||
|
-kb-custom-15 "${copy_menu}"
|
||||||
|
-kb-custom-16 "${help}"
|
||||||
|
-kb-custom-17 "${switch}"
|
||||||
|
-kb-custom-18 "${insert_pass}"
|
||||||
|
-kb-custom-19 "${qrcode}"
|
||||||
|
)
|
||||||
|
args+=( -kb-mode-previous "" # These keyboard shortcut options are needed, because
|
||||||
|
-kb-mode-next "" # Shift+<Left|Right> are otherwise taken by rofi.
|
||||||
|
-select "$entry"
|
||||||
|
-mesg "PW Store: ${root}"
|
||||||
|
-p "passmenu > " )
|
||||||
|
|
||||||
|
selected_password="$(list_passwords 2>/dev/null | _rofi "${args[@]}")"
|
||||||
|
|
||||||
|
rofi_exit=$?
|
||||||
|
if [[ $rofi_exit -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Actions based on exit code, which do not need the entry.
|
||||||
|
# The exit code for -kb-custom-X is X+9.
|
||||||
|
case "${rofi_exit}" in
|
||||||
|
19) roots_index=$(( (roots_index-1+roots_length) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;;
|
||||||
|
20) roots_index=$(( (roots_index+1) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;;
|
||||||
|
25) helpMenu; return;;
|
||||||
|
26) ${basecommand} --bmarks; return;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mapfile -t password_temp < <(PASSWORD_STORE_DIR="${root}" pass show "$selected_password")
|
||||||
|
password=${password_temp[0]}
|
||||||
|
|
||||||
|
if [[ ${password} == "#FILE="* ]]; then
|
||||||
|
pass_file="${password#*=}"
|
||||||
|
mapfile -t password_temp2 < <(PASSWORD_STORE_DIR="${root}" pass show "${pass_file}")
|
||||||
|
password=${password_temp2[0]}
|
||||||
|
fi
|
||||||
|
|
||||||
|
fields=$(printf '%s\n' "${password_temp[@]:1}" | awk '$1 ~ /:$/ || /otpauth:\/\// {$1=$1;print}')
|
||||||
|
declare -A stuff
|
||||||
|
stuff["pass"]=${password}
|
||||||
|
|
||||||
|
if [[ -n $fields ]]; then
|
||||||
|
while read -r LINE; do
|
||||||
|
unset _id _val
|
||||||
|
case "$LINE" in
|
||||||
|
"otpauth://"*|"${OTPmethod_field}"*)
|
||||||
|
_id="OTP"
|
||||||
|
_val=""
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_id="${LINE%%: *}"
|
||||||
|
_val="${LINE#* }"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ -n "$_id" ]]; then
|
||||||
|
stuff["${_id}"]=${_val}
|
||||||
|
fi
|
||||||
|
done < <(printf '%s\n' "${fields}")
|
||||||
|
|
||||||
|
if test "${stuff['autotype']+autotype}"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
stuff["autotype"]="${USERNAME_field} :tab pass"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${stuff["${AUTOTYPE_field}"]}" ]]; then
|
||||||
|
if [[ -n $default_autotype ]]; then
|
||||||
|
stuff["${AUTOTYPE_field}"]="${default_autotype}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ -z "${stuff["${USERNAME_field}"]}" ]]; then
|
||||||
|
if [[ -n $default_user ]]; then
|
||||||
|
if [[ "$default_user" == ":filename" ]]; then
|
||||||
|
stuff["${USERNAME_field}"]="$(basename "$selected_password")"
|
||||||
|
else
|
||||||
|
stuff["${USERNAME_field}"]="${default_user}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)"
|
||||||
|
|
||||||
|
# actions based on keypresses
|
||||||
|
# The exit code for -kb-custom-X is X+9.
|
||||||
|
case "${rofi_exit}" in
|
||||||
|
0) typeMenu;;
|
||||||
|
10) sleep $wait; autopass;;
|
||||||
|
11) sleep $wait; typeUser;;
|
||||||
|
12) sleep $wait; typePass;;
|
||||||
|
13) openURL;;
|
||||||
|
14) copyUser;;
|
||||||
|
15) copyPass;;
|
||||||
|
16) viewEntry;;
|
||||||
|
17) copyURL;;
|
||||||
|
18) default_do="menu" typeMenu;;
|
||||||
|
23) actionMenu;;
|
||||||
|
24) copyMenu;;
|
||||||
|
27) insertPass;;
|
||||||
|
28) generateQrCode;;
|
||||||
|
esac
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clearUp () {
|
||||||
|
password=''
|
||||||
|
selected_password=''
|
||||||
|
unset stuff
|
||||||
|
unset password
|
||||||
|
unset selected_password
|
||||||
|
unset password_temp
|
||||||
|
unset stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
helpMenu () {
|
||||||
|
_rofi -dmenu -mesg "Hint: All hotkeys are configurable in config file" -p "Help > " <<- EOM
|
||||||
|
${autotype}: Autotype
|
||||||
|
${type_user}: Type Username
|
||||||
|
${type_pass}: Type Password
|
||||||
|
${qrcode}: Generate and display qrcode
|
||||||
|
---
|
||||||
|
${copy_name}: Copy Username
|
||||||
|
${copy_pass}: Copy Password
|
||||||
|
${copy_url}: Copy URL
|
||||||
|
${open_url}: Open URL
|
||||||
|
${copy_menu}: Copy Custom Field
|
||||||
|
---
|
||||||
|
${action_menu}: Edit, Move, Delete, Re-generate Submenu
|
||||||
|
${show}: Show Password File
|
||||||
|
${insert_pass}: Insert new Pass Entry
|
||||||
|
${switch}: Switch Pass/Bookmark Mode
|
||||||
|
---
|
||||||
|
${previous_root}: Switch to previous password store (--root)
|
||||||
|
${next_root}: Switch to next password store (--root)
|
||||||
|
EOM
|
||||||
|
help_val=$?
|
||||||
|
|
||||||
|
if [[ $help_val -eq 1 ]]; then
|
||||||
|
exit;
|
||||||
|
else
|
||||||
|
unset helptext; mainMenu;
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typeMenu () {
|
||||||
|
if [[ -n $default_do ]]; then
|
||||||
|
if [[ $default_do == "menu" ]]; then
|
||||||
|
checkIfPass
|
||||||
|
local -a keys=("${!stuff[@]}")
|
||||||
|
keys=("${keys[@]/$AUTOTYPE_field}")
|
||||||
|
typefield=$({ printf '%s' "${AUTOTYPE_field}" ; printf '%s\n' "${keys[@]}" | sort; } | _rofi -dmenu -p "Choose Field to type > ")
|
||||||
|
typefield_exit=$?
|
||||||
|
if [[ $typefield_exit -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
case "$typefield" in
|
||||||
|
'') exit;;
|
||||||
|
'pass') sleep $wait; typePass;;
|
||||||
|
"${AUTOTYPE_field}") sleep $wait; autopass;;
|
||||||
|
*) sleep $wait; typeField
|
||||||
|
esac
|
||||||
|
clearUp
|
||||||
|
elif [[ $default_do == "${AUTOTYPE_field}" ]]; then
|
||||||
|
sleep $wait; autopass
|
||||||
|
else
|
||||||
|
${default_do}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
copyMenu () {
|
||||||
|
checkIfPass
|
||||||
|
copyfield=$(printf '%s\n' "${!stuff[@]}" | sort | _rofi -dmenu -p "Choose Field to copy > ")
|
||||||
|
val=$?
|
||||||
|
if [[ $val -eq 1 ]]; then
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
if [[ $copyfield == "pass" ]]; then
|
||||||
|
copyPass;
|
||||||
|
else
|
||||||
|
copyField
|
||||||
|
fi
|
||||||
|
clearUp
|
||||||
|
}
|
||||||
|
|
||||||
|
actionMenu () {
|
||||||
|
checkIfPass
|
||||||
|
action_content=("< Return"
|
||||||
|
"---"
|
||||||
|
"1 Move Password File"
|
||||||
|
"2 Copy Password File"
|
||||||
|
"3 Delete Password File"
|
||||||
|
"4 Edit Password File"
|
||||||
|
"5 Generate New Password"
|
||||||
|
)
|
||||||
|
|
||||||
|
action=$(printf '%s\n' "${action_content[@]}" | _rofi -dmenu -p "Choose Action > ")
|
||||||
|
if [[ ${action} == "1 Move Password File" ]]; then
|
||||||
|
manageEntry move;
|
||||||
|
elif [[ ${action} == "3 Delete Password File" ]]; then
|
||||||
|
manageEntry delete;
|
||||||
|
elif [[ ${action} == "2 Copy Password File" ]]; then
|
||||||
|
manageEntry copy;
|
||||||
|
elif [[ ${action} == "4 Edit Password File" ]]; then
|
||||||
|
manageEntry edit;
|
||||||
|
elif [[ ${action} == "5 Generate New Password" ]]; then
|
||||||
|
generatePass;
|
||||||
|
elif [[ ${action} == "< Return" ]]; then
|
||||||
|
mainMenu;
|
||||||
|
elif [[ ${action} == "" ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
showEntry () {
|
||||||
|
if [[ -z $pass_content ]]; then
|
||||||
|
pass_temp=$(PASSWORD_STORE_DIR="${root}" pass show "$selected_password")
|
||||||
|
password="${pass_temp%%$'\n'*}"
|
||||||
|
pass_key_value=$(printf '%s\n' "${pass_temp}" | tail -n+2 | grep ': ')
|
||||||
|
declare -A stuff
|
||||||
|
|
||||||
|
while read -r LINE; do
|
||||||
|
_id="${LINE%%: *}"
|
||||||
|
_val="${LINE#* }"
|
||||||
|
stuff["${_id}"]=${_val}
|
||||||
|
done < <(printf '%s\n' "${pass_key_value}")
|
||||||
|
|
||||||
|
stuff["pass"]=${password}
|
||||||
|
|
||||||
|
if test "${stuff['autotype']+autotype}"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
stuff["autotype"]="${USERNAME_field} :tab pass"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bla_content=("< Return"
|
||||||
|
"${pass_content}"
|
||||||
|
)
|
||||||
|
|
||||||
|
bla=$(printf '%s\n' "${bla_content[@]}" | _rofi -dmenu -mesg "Enter: Copy entry to clipboard" -p "> ")
|
||||||
|
rofi_exit=$?
|
||||||
|
|
||||||
|
word=$(printf '%s' "$bla" | gawk -F': ' '{print $1}')
|
||||||
|
|
||||||
|
if [[ ${rofi_exit} -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
elif [[ ${rofi_exit} -eq 0 ]]; then
|
||||||
|
if [[ ${bla} == "< Return" ]]; then
|
||||||
|
mainMenu
|
||||||
|
else
|
||||||
|
if [[ -z $(printf '%s' "${stuff[${word}]}") ]]; then
|
||||||
|
printf '%s' "$word" | doClip
|
||||||
|
else
|
||||||
|
printf '%s' "${stuff[${word}]}" | doClip
|
||||||
|
fi
|
||||||
|
if [[ $notify == "true" ]]; then
|
||||||
|
notify-send "passmenu" "Copied Password\\nClearing in $clip_clear seconds"
|
||||||
|
fi
|
||||||
|
if [[ $notify == "true" ]]; then
|
||||||
|
(sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard | notify-send "passmenu" "Clipboard cleared") &
|
||||||
|
elif [[ $notify == "false" ]]; then
|
||||||
|
(sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard) &
|
||||||
|
fi
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit
|
||||||
|
unset stuff
|
||||||
|
unset password
|
||||||
|
unset selected_password
|
||||||
|
unset password_temp
|
||||||
|
unset stuff
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
manageEntry () {
|
||||||
|
if [[ "$1" == "edit" ]]; then
|
||||||
|
EDITOR=$EDITOR PASSWORD_STORE_DIR="${root}" pass edit "${selected_password}"
|
||||||
|
mainMenu
|
||||||
|
elif [[ $1 == "move" ]]; then
|
||||||
|
cd "${root}" || exit
|
||||||
|
group_array=(*/)
|
||||||
|
group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ")
|
||||||
|
if [[ $group == "" ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
PASSWORD_STORE_DIR="${root}" pass mv "$selected_password" "${group}"
|
||||||
|
mainMenu
|
||||||
|
elif [[ $1 == "copy" ]]; then
|
||||||
|
cd "${root}" || exit
|
||||||
|
group_array=(*/)
|
||||||
|
group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ")
|
||||||
|
if [[ $group == "" ]]; then
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
new_name="$(listgpg | _rofi -dmenu -format 'f' -mesg "Copying to same Group. Please enter a name for the new entry" -p "> ")"
|
||||||
|
fi
|
||||||
|
PASSWORD_STORE_DIR="${root}" pass cp "$selected_password" "${group}/${new_name}"
|
||||||
|
mainMenu
|
||||||
|
elif [[ "$1" == "delete" ]]; then
|
||||||
|
HELP="<span color='$help_color'>Selected entry: ${selected_password}</span>"
|
||||||
|
ask_content=("Yes"
|
||||||
|
"No"
|
||||||
|
)
|
||||||
|
ask=$(printf '%s\n' "${ask_content[@]}" | _rofi -mesg "${HELP}" -dmenu -p "Are You Sure? > ")
|
||||||
|
if [[ "$ask" == "Yes" ]]; then
|
||||||
|
PASSWORD_STORE_DIR="${root}" pass rm --force "${selected_password}"
|
||||||
|
elif [[ "$ask" == "No" ]]; then
|
||||||
|
mainMenu
|
||||||
|
elif [[ -z "$ask" ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
mainMenu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_pass() {
|
||||||
|
if [[ $edit_new_pass == "true" ]]; then
|
||||||
|
PASSWORD_STORE_DIR="${root}" pass edit "${1}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
insertPass () {
|
||||||
|
url=$(_clip_out_clipboard)
|
||||||
|
|
||||||
|
if [[ "${url:0:4}" == "http" ]]; then
|
||||||
|
domain_name="$(printf '%s\n' "${url}" | awk -F / '{l=split($3,a,"."); print (a[l-1]=="com"?a[l-2] OFS:X) a[l-1] OFS a[l]}' OFS=".")"
|
||||||
|
help_content="Domain: ${domain_name}
|
||||||
|
Type name, make sure it is unique"
|
||||||
|
else
|
||||||
|
help_content="Hint: Copy URL to clipboard before calling this menu.
|
||||||
|
Type name, make sure it is unique"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "${root}" || exit
|
||||||
|
group_array=(*/)
|
||||||
|
grouplist=$(printf '%s\n' "${group_array[@]%/}")
|
||||||
|
name="$(listgpg | _rofi -dmenu -format 'f' -filter "${domain_name}" -mesg "${help_content}" -p "> ")"
|
||||||
|
val=$?
|
||||||
|
|
||||||
|
if [[ $val -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
user_content=("${default_user2}"
|
||||||
|
"${USER}"
|
||||||
|
"${default_user}"
|
||||||
|
)
|
||||||
|
|
||||||
|
user=$(printf '%s\n' "${user_content[@]}" | _rofi -dmenu -mesg "Chose Username or type" -p "> ")
|
||||||
|
val=$?
|
||||||
|
|
||||||
|
if [[ $val -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
group_content=("No Group"
|
||||||
|
"---"
|
||||||
|
"${grouplist}"
|
||||||
|
)
|
||||||
|
|
||||||
|
group=$(printf '%s\n' "${group_content[@]}" | _rofi -dmenu -p "Choose Group > ")
|
||||||
|
val=$?
|
||||||
|
|
||||||
|
if [[ $val -eq 1 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
pw=$(printf '%s' "Generate" | _rofi -dmenu -password -p "Password > " -mesg "Type Password or hit Enter to generate one")
|
||||||
|
|
||||||
|
if [[ $pw == "Generate" ]]; then
|
||||||
|
pw=$(_pwgen "${password_length}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
clear
|
||||||
|
|
||||||
|
if [[ "$group" == "No Group" ]]; then
|
||||||
|
if [[ $url == http* ]]; then
|
||||||
|
pass_content=("${pw}"
|
||||||
|
"---"
|
||||||
|
"${USERNAME_field}: ${user}"
|
||||||
|
"${URL_field}: ${url}"
|
||||||
|
)
|
||||||
|
printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}"
|
||||||
|
else
|
||||||
|
pass_content=("${pw}"
|
||||||
|
"---"
|
||||||
|
"${USERNAME_field}: ${user}"
|
||||||
|
)
|
||||||
|
printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ $url == http* ]]; then
|
||||||
|
pass_content=("${pw}"
|
||||||
|
"---"
|
||||||
|
"${USERNAME_field}: ${user}"
|
||||||
|
"${URL_field}: ${url}"
|
||||||
|
)
|
||||||
|
printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}"
|
||||||
|
else
|
||||||
|
pass_content=("${pw}"
|
||||||
|
"---"
|
||||||
|
"${USERNAME_field}: ${user}"
|
||||||
|
)
|
||||||
|
printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
help_msg () {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage:
|
||||||
|
passmenu [command]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
--insert insert new entry to password store
|
||||||
|
--root set custom root directories (colon separated)
|
||||||
|
--last-used highlight last used item
|
||||||
|
--show-last show details of last used Entry
|
||||||
|
--bmarks start in bookmarks mode
|
||||||
|
|
||||||
|
passmenu version 1.0.0
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
get_config_file () {
|
||||||
|
configs=("$ROFI_PASS_CONFIG"
|
||||||
|
"$config_dir/rofi/passmenu/config"
|
||||||
|
"/etc/passmenu.conf")
|
||||||
|
|
||||||
|
# return the first config file with a valid path
|
||||||
|
for config in "${configs[@]}"; do
|
||||||
|
# '-n' is needed in case ROFI_PASS_CONFIG is not set
|
||||||
|
if [[ -n "${config}" && -f "${config}" ]]; then
|
||||||
|
printf "%s" "$config"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main () {
|
||||||
|
# load config file
|
||||||
|
config_file="$(get_config_file)"
|
||||||
|
[[ -n "$config_file" ]] && source "$config_file"
|
||||||
|
|
||||||
|
# create tmp dir
|
||||||
|
if [[ ! -d "$cache_dir/passmenu" ]]; then
|
||||||
|
mkdir -p "$cache_dir/passmenu"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# fix keyboard layout if enabled in config
|
||||||
|
if [[ $fix_layout == "true" ]]; then
|
||||||
|
layout_cmd
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set help color
|
||||||
|
if [[ $help_color == "" ]]; then
|
||||||
|
help_color=$(rofi -dump-xresources | grep 'rofi.color.normal' | gawk -F ',' '/,/{gsub(/ /, "", $2); print $2}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check for BROWSER variable, use xdg-open as fallback
|
||||||
|
if [[ -z $BROWSER ]]; then
|
||||||
|
export BROWSER=xdg-open
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check if alternative root directory was given on commandline
|
||||||
|
if [[ -r "$cache_dir/passmenu/last_used" ]] && [[ $1 == "--last-used" || $1 == "--show-last" ]]; then
|
||||||
|
roots=("$(awk -F ': ' '{ print $1 }' "$cache_dir/passmenu/last_used")")
|
||||||
|
elif [[ -n "$2" && "$1" == "--root" ]]; then
|
||||||
|
custom_root=true; IFS=: read -r -a roots <<< "$2"
|
||||||
|
elif [[ -n $root ]]; then
|
||||||
|
custom_root=true; IFS=: read -r -a roots <<< "${root}"
|
||||||
|
elif [[ -n ${PASSWORD_STORE_DIR} ]]; then
|
||||||
|
roots=("${PASSWORD_STORE_DIR}")
|
||||||
|
else
|
||||||
|
roots=("$HOME/.password-store")
|
||||||
|
fi
|
||||||
|
roots_index=0
|
||||||
|
roots_length=${#roots[@]}
|
||||||
|
export root=${roots[$roots_index]}
|
||||||
|
export PASSWORD_STORE_DIR="${root}"
|
||||||
|
case $1 in
|
||||||
|
--insert)
|
||||||
|
insertPass
|
||||||
|
;;
|
||||||
|
--root)
|
||||||
|
mainMenu
|
||||||
|
;;
|
||||||
|
--help)
|
||||||
|
help_msg
|
||||||
|
;;
|
||||||
|
--last-used)
|
||||||
|
if [[ -r "$cache_dir/passmenu/last_used" ]]; then
|
||||||
|
entry="$(awk -F ': ' '{ print $2 }' "$cache_dir/passmenu/last_used")"
|
||||||
|
fi
|
||||||
|
mainMenu
|
||||||
|
;;
|
||||||
|
--show-last)
|
||||||
|
if [[ -r "$cache_dir/passmenu/last_used" ]]; then
|
||||||
|
selected_password="$(awk -F ': ' '{ print $2 }' "$cache_dir/passmenu/last_used")" viewEntry
|
||||||
|
else
|
||||||
|
mainMenu
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--bmarks)
|
||||||
|
mainMenu --bmarks;
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
mainMenu
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
263
.local/bin/rofi/powermenu
Executable file
263
.local/bin/rofi/powermenu
Executable file
@ -0,0 +1,263 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# powermenu
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 07 December 2022, 11:01:52 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Sat 02 September 2023, 12:11:51 AM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# This script defines just a mode for rofi instead of being a self-contained
|
||||||
|
# executable that launches rofi by itself. This makes it more flexible than
|
||||||
|
# running rofi inside this script as now the user can call rofi as one pleases.
|
||||||
|
# For instance:
|
||||||
|
#
|
||||||
|
# rofi -show powermenu -config ~/.config/rofi/powermenu.rasi
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# <None>
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
if [ -z "${ROFI_OUTSIDE}" ]; then
|
||||||
|
echo "run this script in rofi".
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -u
|
||||||
|
|
||||||
|
# All supported choices
|
||||||
|
all=(shutdown reboot suspend hibernate logout lockscreen)
|
||||||
|
|
||||||
|
# By default, show all (i.e., just copy the array)
|
||||||
|
show=("${all[@]}")
|
||||||
|
|
||||||
|
declare -A texts
|
||||||
|
texts[lockscreen]="lock screen"
|
||||||
|
texts[logout]="log out"
|
||||||
|
texts[suspend]="suspend"
|
||||||
|
texts[hibernate]="hibernate"
|
||||||
|
texts[reboot]="reboot"
|
||||||
|
texts[shutdown]="shut down"
|
||||||
|
|
||||||
|
declare -A icons
|
||||||
|
icons[lockscreen]=""
|
||||||
|
icons[logout]=""
|
||||||
|
icons[suspend]=""
|
||||||
|
icons[hibernate]=""
|
||||||
|
icons[reboot]=""
|
||||||
|
icons[shutdown]=""
|
||||||
|
icons[cancel]=""
|
||||||
|
|
||||||
|
declare -A actions
|
||||||
|
actions[lockscreen]="loginctl lock-session"
|
||||||
|
actions[logout]="pkill -KILL -u ${USER}"
|
||||||
|
actions[suspend]="systemctl suspend"
|
||||||
|
actions[hibernate]="systemctl hibernate"
|
||||||
|
actions[reboot]="systemctl reboot"
|
||||||
|
actions[shutdown]="systemctl poweroff"
|
||||||
|
|
||||||
|
# By default, ask for confirmation for actions that are irreversible
|
||||||
|
confirmations=(reboot shutdown logout)
|
||||||
|
|
||||||
|
# By default, no dry run
|
||||||
|
dryrun=false
|
||||||
|
showsymbols=true
|
||||||
|
|
||||||
|
getuptime() {
|
||||||
|
state=$(uptime | sed -E 's/^[^,]*up *//; s/mins/minutes/; s/hrs?/hours/;
|
||||||
|
s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/;
|
||||||
|
s/^1 hours/1 hour/; s/ 1 hours/ 1 hour/;
|
||||||
|
s/min,/minutes,/; s/ 0 minutes,/ less than a minute,/; s/ 1 minutes/ 1 minute/;
|
||||||
|
s/ / /; s/, *[[:digit:]]* users?.*//')
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_valid {
|
||||||
|
option="$1"
|
||||||
|
shift 1
|
||||||
|
for entry in "${@}"; do
|
||||||
|
if [ -z "${actions[$entry]+x}" ]; then
|
||||||
|
echo "Invalid choice in $option: $entry" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse command-line options
|
||||||
|
parsed=$(getopt --options=h --longoptions=help,dry-run,confirm:,choices:,choose:,symbols,no-symbols --name "$0" -- "$@")
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo 'Terminating...' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
eval set -- "$parsed"
|
||||||
|
unset parsed
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
"-h" | "--help")
|
||||||
|
echo "rofi-power-menu - a power menu mode for Rofi"
|
||||||
|
echo
|
||||||
|
echo "Usage: rofi-power-menu [--choices CHOICES] [--confirm CHOICES]"
|
||||||
|
echo " [--choose CHOICE] [--dry-run] [--symbols|--no-symbols]"
|
||||||
|
echo
|
||||||
|
echo "Use with Rofi in script mode. For instance, to ask for shutdown or reboot:"
|
||||||
|
echo
|
||||||
|
echo " rofi -show menu -modi \"menu:rofi-power-menu --choices=shutdown/reboot\""
|
||||||
|
echo
|
||||||
|
echo "Available options:"
|
||||||
|
echo " --dry-run Don't perform the selected action but print it to stderr."
|
||||||
|
echo " --choices CHOICES Show only the selected choices in the given order. Use / "
|
||||||
|
echo " as the separator. Available choices are lockscreen, logout,"
|
||||||
|
echo " suspend, hibernate, reboot and shutdown. By default, all"
|
||||||
|
echo " available choices are shown."
|
||||||
|
echo " --confirm CHOICES Require confirmation for the gives choices only. Use / as"
|
||||||
|
echo " the separator. Available choices are lockscreen, logout,"
|
||||||
|
echo " suspend, hibernate, reboot and shutdown. By default, only"
|
||||||
|
echo " irreversible actions logout, reboot and shutdown require"
|
||||||
|
echo " confirmation."
|
||||||
|
echo " --choose CHOICE Preselect the given choice and only ask for a confirmation"
|
||||||
|
echo " (if confirmation is set to be requested). It is strongly"
|
||||||
|
echo " recommended to combine this option with --confirm=CHOICE"
|
||||||
|
echo " if the choice wouldn't require confirmation by default."
|
||||||
|
echo " Available choices are lockscreen, logout, suspend,"
|
||||||
|
echo " hibernate, reboot and shutdown."
|
||||||
|
echo " --[no-]symbols Show Unicode symbols or not. Requires a font with support"
|
||||||
|
echo " for the symbols. Use, for instance, fonts from the"
|
||||||
|
echo " Nerdfonts collection. By default, they are shown"
|
||||||
|
echo " -h,--help Show this help text."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
"--dry-run")
|
||||||
|
dryrun=true
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
"--confirm")
|
||||||
|
IFS='/' read -ra confirmations <<<"$2"
|
||||||
|
check_valid "$1" "${confirmations[@]}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
"--choices")
|
||||||
|
IFS='/' read -ra show <<<"$2"
|
||||||
|
check_valid "$1" "${show[@]}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
"--choose")
|
||||||
|
# Check that the choice is valid
|
||||||
|
check_valid "$1" "$2"
|
||||||
|
selectionID="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
"--symbols")
|
||||||
|
showsymbols=true
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
"--no-symbols")
|
||||||
|
showsymbols=false
|
||||||
|
shift 1
|
||||||
|
;;
|
||||||
|
"--")
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Internal error" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Define the messages after parsing the CLI options so that it is possible to
|
||||||
|
# configure them in the future.
|
||||||
|
|
||||||
|
function write_message {
|
||||||
|
icon="<span font_size=\"medium\">$1</span>"
|
||||||
|
text="<span font_size=\"medium\">$2</span>"
|
||||||
|
if [ "$showsymbols" = "true" ]; then
|
||||||
|
echo -n "\u200e$icon \u2068$text\u2069"
|
||||||
|
else
|
||||||
|
echo -n "$text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_selection {
|
||||||
|
echo -e "$1" | $(
|
||||||
|
read -r -d '' entry
|
||||||
|
echo "echo $entry"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -A messages
|
||||||
|
declare -A confirmationMessages
|
||||||
|
for entry in "${all[@]}"; do
|
||||||
|
messages[$entry]=$(write_message "${icons[$entry]}" "${texts[$entry]^}")
|
||||||
|
done
|
||||||
|
for entry in "${all[@]}"; do
|
||||||
|
confirmationMessages[$entry]=$(write_message "${icons[$entry]}" "Yes, ${texts[$entry]}")
|
||||||
|
done
|
||||||
|
confirmationMessages[cancel]=$(write_message "${icons[cancel]}" "No, cancel")
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
# If arguments given, use those as the selection
|
||||||
|
selection="${*}"
|
||||||
|
else
|
||||||
|
# Otherwise, use the CLI passed choice if given
|
||||||
|
if [ -n "${selectionID+x}" ]; then
|
||||||
|
selection="${messages[$selectionID]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Don't allow custom entries
|
||||||
|
echo -e "\0no-custom\x1ftrue"
|
||||||
|
|
||||||
|
echo -e "\0markup-rows\x1ftrue"
|
||||||
|
|
||||||
|
if [ -z "${selection+x}" ]; then
|
||||||
|
getuptime
|
||||||
|
echo -e "\0prompt\x1fPower Menu"
|
||||||
|
echo -en "\0message\x1fUptime :: ${state^}\n"
|
||||||
|
for entry in "${show[@]}"; do
|
||||||
|
echo -e "${messages[$entry]}\0icon\x1f${icons[$entry]}"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
for entry in "${show[@]}"; do
|
||||||
|
if [ "$selection" = "$(print_selection "${messages[$entry]}")" ]; then
|
||||||
|
# Check if the selected entry is listed in confirmation requirements
|
||||||
|
for confirmation in "${confirmations[@]}"; do
|
||||||
|
if [ "$entry" = "$confirmation" ]; then
|
||||||
|
# Ask for confirmation
|
||||||
|
echo -e "\0prompt\x1fAre you sure"
|
||||||
|
echo -e "${confirmationMessages[$entry]}\0icon\x1f${icons[$entry]}"
|
||||||
|
echo -e "${confirmationMessages[cancel]}\0icon\x1f${icons[cancel]}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# If not, then no confirmation is required, so mark confirmed
|
||||||
|
selection=$(print_selection "${confirmationMessages[$entry]}")
|
||||||
|
fi
|
||||||
|
if [ "$selection" = "$(print_selection "${confirmationMessages[$entry]}")" ]; then
|
||||||
|
if [ $dryrun = true ]; then
|
||||||
|
# Tell what would have been done
|
||||||
|
echo "Selected: $entry" >&2
|
||||||
|
else
|
||||||
|
# Perform the action
|
||||||
|
${actions[$entry]}
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "$selection" = "$(print_selection "${confirmationMessages[cancel]}")" ]; then
|
||||||
|
# Do nothing
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# The selection didn't match anything, so raise an error
|
||||||
|
echo "Invalid selection: $selection" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
25
.local/bin/rofi/powermenu_run
Executable file
25
.local/bin/rofi/powermenu_run
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# powermenu_run
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 07 December 2022, 11:01:52 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Sun 06 August 2023, 11:40:51 AM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# <None>
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
rofi -show powermenu -config ~/.config/rofi/powermenu.rasi
|
224
.local/bin/rofi/wifimanager
Executable file
224
.local/bin/rofi/wifimanager
Executable file
@ -0,0 +1,224 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# wifimanager
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 07 December 2022, 11:01:26 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Mon 16 January 2023, 05:33:28 PM [GMT]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# rofi nmcli(Network Manager) dunst
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
if [ -z "${ROFI_OUTSIDE}" ]
|
||||||
|
then
|
||||||
|
echo "run this script in rofi".
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
CACHE_DIR="$HOME/.cache/rofi/network"
|
||||||
|
[ ! -d $CACHE_DIR ] && mkdir -p "$CACHE_DIR"
|
||||||
|
|
||||||
|
station=$(nmcli device | grep "wifi " | awk '{ printf $1 }') # Device Station name
|
||||||
|
|
||||||
|
function network_status () {
|
||||||
|
state=$(nmcli -fields WIFI g | sed -n 2p | xargs) # Wifi enbaled or disabled
|
||||||
|
connection=$(nmcli connection show --active | grep "wifi" | awk -F "wifi" '{ print $1 }' | sed -e '1{s/[^ ]\+\s*$//}')
|
||||||
|
}
|
||||||
|
|
||||||
|
# Don't allow custom entries
|
||||||
|
echo -e "\0no-custom\x1ftrue"
|
||||||
|
|
||||||
|
function network_options() {
|
||||||
|
if [[ "$state" == "enabled" ]]; then
|
||||||
|
if [ -z "$connection" ]; then
|
||||||
|
echo "-=|> Connect to a Network"
|
||||||
|
echo "-=|> Connect to a Hidden Network"
|
||||||
|
connection="N/A"
|
||||||
|
elif [ ! -z "$connection" ]; then
|
||||||
|
echo "-=|> Connect to a Network"
|
||||||
|
echo "-=|> Connect to a Hidden Network"
|
||||||
|
echo "-=|> Disconnect Current Network"
|
||||||
|
fi
|
||||||
|
echo "-=|> Disable Wifi"
|
||||||
|
elif [[ "$state" =~ "disabled" ]]; then
|
||||||
|
echo "-=|> Enable Wifi"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function main_scrn() { # Main window to be displayed on start
|
||||||
|
rm $CACHE_DIR/* >/dev/null 2>&1
|
||||||
|
network_status
|
||||||
|
network_options
|
||||||
|
echo -en "\x00prompt\x1fWifi\n"
|
||||||
|
echo -en "\0message\x1fWIFI Status :: ${state^}\t\tConnected to :: ${connection}\n"
|
||||||
|
echo -en "-=|> Delete existing connections\n"
|
||||||
|
echo -en "-=|> Refresh\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_available_networks(){ # Output the list of available Wifi networks
|
||||||
|
nmcli -f BSSID,SSID device wifi list > "$CACHE_DIR/available_networks"
|
||||||
|
echo -en "\0message\x1f $(cat "$CACHE_DIR/available_networks" | sed '1!d')\n"
|
||||||
|
echo "$(cat "$CACHE_DIR/available_networks" | sed '1d')"
|
||||||
|
echo "-=|> Rescan available networks"
|
||||||
|
echo "<|=- To Previous Menu"
|
||||||
|
}
|
||||||
|
|
||||||
|
function saved_connections() {
|
||||||
|
echo "delete_network" > "$CACHE_DIR/delete_connection"
|
||||||
|
echo -en "\0message\x1f\t\t\t\t\t\t[Enter] - Delete selected connection\n"
|
||||||
|
echo -en "UUID\t\t\t\t TYPE\tNAME\0nonselectable\x1ftrue\n"
|
||||||
|
echo -en "\0active\x1f0\n"
|
||||||
|
nmcli --fields UUID,TYPE,NAME con show | grep wifi
|
||||||
|
echo "<|=- To Previous Menu"
|
||||||
|
}
|
||||||
|
|
||||||
|
function error_status() {
|
||||||
|
if [[ "$status" =~ "Connection successfully activated " ]]; then # If connected successfully, then notify and exit
|
||||||
|
notify-send "Connected to " "$SSID"
|
||||||
|
exit 0
|
||||||
|
elif [[ "$status" =~ "Device '$station' successfully activated with " ]]; then # If connected successfully, then notify and exit
|
||||||
|
notify-send "Network " "$(echo $status | cut -c 6-)"
|
||||||
|
rm $CACHE_DIR/*
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
if [ -f $CACHE_DIR/network_ssid ]; then
|
||||||
|
echo -en "\0message\x1f! Connection Failed. This is normal if connecting to a hidden network, Try again.\n"
|
||||||
|
elif [ -f $CACHE_DIR/network_bssid ]; then
|
||||||
|
echo -en "\0message\x1f! Connection Failed! Check your password.\n"
|
||||||
|
fi
|
||||||
|
echo "$status" # If failed to connect, shows the error code and asks for password
|
||||||
|
echo "<|=- To Previous Menu"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_ssid() {
|
||||||
|
echo "hidden" > "$CACHE_DIR/get_ssid"
|
||||||
|
echo -en "\00prompt\x1fEnter SSID\n"
|
||||||
|
echo -en "\0message\x1f# Type your network SSID (Network Name).\n"
|
||||||
|
echo "<|=- To Previous Menu"
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_password() {
|
||||||
|
echo -en "\00prompt\x1fPassword\n"
|
||||||
|
echo -en "\0message\x1f# Type your network security passphrase here.\n"
|
||||||
|
echo "<|=- To Previous Menu"
|
||||||
|
}
|
||||||
|
|
||||||
|
function restore_view() {
|
||||||
|
if [ -f $CACHE_DIR/delete_connection ]; then
|
||||||
|
saved_connections
|
||||||
|
elif [ -f $CACHE_DIR/available_networks ]; then
|
||||||
|
show_available_networks
|
||||||
|
elif [ -f $CACHE_DIR/network_selected ]; then
|
||||||
|
show_available_networks
|
||||||
|
elif [ -f $CACHE_DIR/get_ssid ]; then
|
||||||
|
get_ssid
|
||||||
|
elif [ -f $CACHE_DIR/network_bssid ] || [ -f $CACHE_DIR/network_ssid ]; then
|
||||||
|
get_password
|
||||||
|
else
|
||||||
|
main_scrn
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_previous_menu() {
|
||||||
|
if [ -f $CACHE_DIR/network_bssid ]; then
|
||||||
|
rm $CACHE_DIR/network_bssid
|
||||||
|
show_available_networks
|
||||||
|
elif [ -f $CACHE_DIR/network_ssid ]; then
|
||||||
|
rm $CACHE_DIR/network_ssid
|
||||||
|
get_ssid
|
||||||
|
else
|
||||||
|
main_scrn
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${ROFI_RETV}" == "0" ]]; then
|
||||||
|
main_scrn
|
||||||
|
|
||||||
|
elif [[ "${ROFI_RETV}" == "1" ]]; then
|
||||||
|
if [[ "$@" == "<|=- To Previous Menu" ]]; then
|
||||||
|
show_previous_menu
|
||||||
|
elif [[ "$@" == "-=|> Refresh" ]]; then
|
||||||
|
main_scrn
|
||||||
|
elif [[ "$@" == "-=|> Disable Wifi" ]]; then
|
||||||
|
nmcli radio wifi off
|
||||||
|
restore_view
|
||||||
|
elif [[ "$@" == "-=|> Enable Wifi" ]]; then
|
||||||
|
nmcli radio wifi on
|
||||||
|
restore_view
|
||||||
|
elif [[ "$@" == "-=|> Connect to a Network" ]]; then
|
||||||
|
show_available_networks
|
||||||
|
elif [[ "$@" == "-=|> Connect to a Hidden Network" ]]; then
|
||||||
|
get_ssid
|
||||||
|
elif [[ "$@" == "-=|> Disconnect Current Network" ]]; then # Disconnect any connected network
|
||||||
|
con_uuid=$(nmcli connection show --active | grep "wifi" | awk -F "wifi" '{print $1}' | awk '{print $NF}' | xargs)
|
||||||
|
nmcli connection down "$con_uuid" > /dev/null 2>&1
|
||||||
|
restore_view
|
||||||
|
elif [[ "$@" == "-=|> Delete existing connections" ]]; then # Opens the saved networks list window
|
||||||
|
saved_connections
|
||||||
|
elif [ -f $CACHE_DIR/delete_connection ]; then
|
||||||
|
connection_uuid=$(echo "$@" | awk '{print $1}')
|
||||||
|
nmcli con delete uuid "$connection_uuid" >/dev/null 2>&1
|
||||||
|
restore_view
|
||||||
|
elif [ -f $CACHE_DIR/available_networks ]; then
|
||||||
|
if [[ "$@" == "-=|> Rescan available networks" ]]; then
|
||||||
|
nmcli device wifi rescan >/dev/null 2>&1
|
||||||
|
restore_view
|
||||||
|
else
|
||||||
|
rm $CACHE_DIR/available_networks
|
||||||
|
echo "$@" > "$CACHE_DIR/network_selected"
|
||||||
|
BSSID=$(echo "$@" | awk '{print $1}')
|
||||||
|
SSID=$(echo "$@" | cut -c 20- | xargs)
|
||||||
|
SECURITY=$(nmcli -f BSSID,SECURITY device wifi | grep "$BSSID" | awk -F " " '{print $2}' | xargs)
|
||||||
|
if [[ $(nmcli -f NAME con show | grep -o -m 1 "$SSID") == "$SSID" ]]; then
|
||||||
|
status=$(nmcli connection up "$SSID")
|
||||||
|
error_status
|
||||||
|
exit 0
|
||||||
|
elif [[ "$SECURITY" =~ "--" ]] || [ -z "$SECURITY" ]; then
|
||||||
|
nmcli device wifi connect "$BSSID" >/dev/null 2>&1
|
||||||
|
notify-send "Successfully connected to " "$SSID"
|
||||||
|
elif [[ "$SECURITY" =~ "WPA" ]] || [ "$SECURITY" =~ "WEP" ]; then
|
||||||
|
rm $CACHE_DIR/network_selected
|
||||||
|
echo "$BSSID" > "$CACHE_DIR/network_bssid"
|
||||||
|
get_password
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "${ROFI_RETV}" == "2" ]]; then
|
||||||
|
if [ -f $CACHE_DIR/get_ssid ]; then
|
||||||
|
rm $CACHE_DIR/get_ssid
|
||||||
|
echo "$@" > "$CACHE_DIR/network_ssid"
|
||||||
|
get_password
|
||||||
|
elif [ -f $CACHE_DIR/network_bssid ]; then
|
||||||
|
PASSWORD="$@"
|
||||||
|
BSSID=$(cat "$CACHE_DIR/network_bssid")
|
||||||
|
status=$(nmcli device wifi connect "$BSSID" password "$PASSWORD")
|
||||||
|
error_status
|
||||||
|
elif [ -f $CACHE_DIR/network_ssid ]; then
|
||||||
|
PASSWORD="$@"
|
||||||
|
SSID=$(cat "$CACHE_DIR/network_ssid")
|
||||||
|
status=$(nmcli device wifi connect "$SSID" password "$PASSWORD" hidden true)
|
||||||
|
error_status
|
||||||
|
else
|
||||||
|
restore_view
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
restore_view
|
||||||
|
echo -en "\0message\x1f# Custom key events are not valid in this mode.\n"
|
||||||
|
|
||||||
|
fi
|
25
.local/bin/rofi/wifimanager_run
Executable file
25
.local/bin/rofi/wifimanager_run
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# wifimanager_run
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Wed 07 December 2022, 11:01:52 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Sat 16 September 2023, 07:42:10 PM [UTC]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# <Todo>
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# <None>
|
||||||
|
#
|
||||||
|
# shellcheck disable=all
|
||||||
|
|
||||||
|
rofi -show wifimanager -config ~/.config/rofi/wifimanager.rasi
|
269
.local/bin/rofi/youtube_subs
Executable file
269
.local/bin/rofi/youtube_subs
Executable file
@ -0,0 +1,269 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#-*-coding:utf-8 -*-
|
||||||
|
#Auto updated?
|
||||||
|
# Yes
|
||||||
|
#File:
|
||||||
|
# youtube_subs
|
||||||
|
#Author:
|
||||||
|
# The-Repo-Club [wayne6324@gmail.com]
|
||||||
|
#Github:
|
||||||
|
# https://github.com/The-Repo-Club/
|
||||||
|
#
|
||||||
|
#Created:
|
||||||
|
# Sun 03 January 2021, 05:09:33 PM [GMT]
|
||||||
|
#Modified:
|
||||||
|
# Fri 25 August 2023, 05:45:05 PM [GMT+1]
|
||||||
|
#
|
||||||
|
#Description:
|
||||||
|
# Watch your youtube subscriptions without a youtube account
|
||||||
|
# via curl, repomenu, browser and basic unix commands.
|
||||||
|
#
|
||||||
|
# The $SUBS_FILE is a text file containing usernames or channel IDs
|
||||||
|
# comments and blank lines are ignored.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#Dependencies:
|
||||||
|
# rofi
|
||||||
|
#
|
||||||
|
|
||||||
|
rofis() {
|
||||||
|
rofi -dmenu -p "Youtube Subs:" -mesg "Select a video."
|
||||||
|
}
|
||||||
|
|
||||||
|
# -/-/-/-/- Settings -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
||||||
|
: "${SUBS_FILE:=${HOME}/.config/rofi/configs/subs.ini}"
|
||||||
|
: "${SUBS_MENU_PROG:=rofis}"
|
||||||
|
: "${SUBS:=${HOME}/.cache/subs}"
|
||||||
|
: "${SUBS_LINKS:=$SUBS/links}"
|
||||||
|
: "${SUBS_CACHE:=$SUBS/cache}"
|
||||||
|
: "${SUBS_SLEEP_VALUE:=0.05}" # raise this if you experience problems
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SEP=^^^^^ # shouldn't need to change this
|
||||||
|
SUBS_OPEN="mpv --volume=50"
|
||||||
|
# -/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf >&2 '%s\n' "$*"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
die 'Usage: youtube_subs [-c cat_subs] [-g gen_links] [-u update_subs] [-d daemonize]'
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Synopsis: $SUBS_FILE [txt] -> $SUBS_LINKS [xml links]
|
||||||
|
#
|
||||||
|
# Updates local cache of xml subscription links from the
|
||||||
|
# subscription file containing either usernames or channel ids.
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
gen_links() {
|
||||||
|
: >"$SUBS_LINKS"
|
||||||
|
|
||||||
|
count=0
|
||||||
|
total=$(sed -e '/^$/d' -e '/^#/d' <"$SUBS_FILE" | wc -l)
|
||||||
|
|
||||||
|
while read -r line; do
|
||||||
|
|
||||||
|
# ignore comments and blank lines
|
||||||
|
case $line in '' | ' ' | '#'*) continue ;; esac
|
||||||
|
|
||||||
|
# strip off in-line comments and any trailing whitespace
|
||||||
|
line=${line%%#*}
|
||||||
|
line=${line%% *}
|
||||||
|
|
||||||
|
count=$((count + 1))
|
||||||
|
|
||||||
|
case $line in
|
||||||
|
UC*)
|
||||||
|
# YT channel IDs always begin with 'UC' and are 24 chars long
|
||||||
|
printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line"
|
||||||
|
|
||||||
|
[ ${#line} -eq 24 ] &&
|
||||||
|
printf 'https://youtube.com/feeds/videos.xml?%s\n' \
|
||||||
|
"channel_id=$line" >>"$SUBS_LINKS"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# otherwise we are given a username, we must find out its channel ID
|
||||||
|
printf "fetching channel ID for %s...\n" "$line"
|
||||||
|
|
||||||
|
curl -sfL --retry 10 "https://youtube.com/user/$line/about" |
|
||||||
|
while read -r line; do
|
||||||
|
case $line in
|
||||||
|
*channel/UC??????????????????????*)
|
||||||
|
line=${line##*channel/}
|
||||||
|
line=${line%%\"*}
|
||||||
|
printf "[%s/%s] using channel ID '%s' for xml link\n" "$count" "$total" "$line"
|
||||||
|
printf 'https://youtube.com/feeds/videos.xml?channel_id=%s\n' \
|
||||||
|
"$line" >>"$SUBS_LINKS"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done &
|
||||||
|
sleep "${SUBS_SLEEP_VALUE:-0}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
done <"$SUBS_FILE"
|
||||||
|
|
||||||
|
count=0
|
||||||
|
while [ "$count" -ne "$total" ]; do
|
||||||
|
count=$(wc -l <"$SUBS_LINKS")
|
||||||
|
printf "[%s/%s] waiting for jobs to complete...\n" "$count" "$total"
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Synopsis: $1 [LINK] -> $SUBS_CACHE/$chan_name/concat [CHANNEL INFO]
|
||||||
|
#
|
||||||
|
# Takes a channel rss feed link and creates a file
|
||||||
|
# with a line of its videos dates, titles, and urls.
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
get_vids() {
|
||||||
|
data=$(curl -sfL --retry 15 "$1")
|
||||||
|
|
||||||
|
# hide the first <published> tag which is the channel
|
||||||
|
# creation date
|
||||||
|
data=${data#*\<\/published\>}
|
||||||
|
|
||||||
|
# trim off outer <name> tags
|
||||||
|
chan=${data%%</name*}
|
||||||
|
chan=${chan##*name>}
|
||||||
|
|
||||||
|
printf "%s\n" "$data" |
|
||||||
|
while read -r line; do
|
||||||
|
case $line in
|
||||||
|
*'link rel='*)
|
||||||
|
line=${line#*href=\"}
|
||||||
|
line=${line%\"/\>}
|
||||||
|
line=https://${line#*www.}
|
||||||
|
url=$line
|
||||||
|
;;
|
||||||
|
*'<published>'*)
|
||||||
|
line=${line%+00:*}
|
||||||
|
line=${line#*<published>}
|
||||||
|
date=$line
|
||||||
|
;;
|
||||||
|
*'<media:title>'*)
|
||||||
|
line=${line%</*}
|
||||||
|
line=${line#*:title>}
|
||||||
|
title=$line
|
||||||
|
printf '%s\n' \
|
||||||
|
"${date}${SEP}${chan}${SEP}${title}${SEP}${url}" \
|
||||||
|
>>"$SUBS_CACHE/$chan"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Updates the local cache of subscriptions. ([-u] flag)
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
update_subs() {
|
||||||
|
[ -f "$SUBS_LINKS" ] || die 'Subs links have not been generated.'
|
||||||
|
|
||||||
|
rm -r "${SUBS_CACHE:-?}" 2>/dev/null || :
|
||||||
|
mkdir -p "$SUBS_CACHE"
|
||||||
|
|
||||||
|
total=$(wc -l <"$SUBS_LINKS")
|
||||||
|
|
||||||
|
count=0
|
||||||
|
while read -r link; do
|
||||||
|
count=$((count + 1))
|
||||||
|
printf 'starting job [%s/%s] for %s\n' "$count" "$total" "$link"
|
||||||
|
get_vids "$link" &
|
||||||
|
sleep "${SUBS_SLEEP_VALUE:-0}"
|
||||||
|
done <"$SUBS_LINKS"
|
||||||
|
|
||||||
|
count=0
|
||||||
|
while [ "$count" -ne "$total" ]; do
|
||||||
|
count=$(printf '%s\n' "$SUBS_CACHE"/* | wc -l)
|
||||||
|
printf "[%s/%s] waiting for fetch jobs to complete...\n" "$count" "$total"
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s\n\n' 'done!'
|
||||||
|
}
|
||||||
|
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
# Grab current cache of subscriptions, sort by date uploaded
|
||||||
|
# -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
cat_subs() {
|
||||||
|
sort -r "$SUBS_CACHE"/* |
|
||||||
|
while read -r line; do
|
||||||
|
chan=${line#*$SEP}
|
||||||
|
chan=${chan%%$SEP*}
|
||||||
|
title=${line#*$chan$SEP}
|
||||||
|
title=${title%%$SEP*}
|
||||||
|
date=${line%%$SEP*}
|
||||||
|
date=${date#*-}
|
||||||
|
date=${date%T*}
|
||||||
|
printf '[%s %s] %s\n' "$date" "$chan" "$title"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Split the concatenated lines into entities, send to menu program.
|
||||||
|
# Finally, play the result with mpv.
|
||||||
|
get_sel() {
|
||||||
|
if [ -d "$SUBS_CACHE" ]; then
|
||||||
|
sel=$(cat_subs | $SUBS_MENU_PROG)
|
||||||
|
else
|
||||||
|
die 'Subs cache has not been retrieved.'
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$sel" ] || die Interrupted
|
||||||
|
|
||||||
|
chan="${sel#* }"
|
||||||
|
chan="${chan%%] *}"
|
||||||
|
title=${sel#*"$chan"\] }
|
||||||
|
while read -r line; do
|
||||||
|
case $line in
|
||||||
|
*"$SEP$title$SEP"*)
|
||||||
|
url=${line##*$SEP}
|
||||||
|
if [ "$url" ]; then
|
||||||
|
printf 'playing: %s\n' "$url"
|
||||||
|
# Play the selection.
|
||||||
|
# shellcheck disable=2086
|
||||||
|
exec $SUBS_OPEN "$url"
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <"$SUBS_CACHE/$chan"
|
||||||
|
}
|
||||||
|
|
||||||
|
daemonize() {
|
||||||
|
# create a cached copy of the subs file to check for changes
|
||||||
|
# if changes occur, re-generate links automatically
|
||||||
|
daemon_file=${HOME}/.cache/subs_daemon.cache
|
||||||
|
if [ ! -f "$daemon_file" ]; then
|
||||||
|
cp -f "${SUBS_FILE:=${HOME}/.config/repomenu/subs.ini}" "$daemon_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if ! cmp "${SUBS_FILE:=${HOME}/.config/repomenu/subs.ini}" "$daemon_file"; then
|
||||||
|
cp -f "${SUBS_FILE:=${HOME}/.config/repomenu/subs.ini}" "$daemon_file"
|
||||||
|
fi
|
||||||
|
gen_links
|
||||||
|
update_subs
|
||||||
|
interval=${SUBS_DAEMON_INTERVAL:-$((10 * 60))}
|
||||||
|
printf 'Sleeping for %s seconds...\n' "$interval"
|
||||||
|
sleep "$interval"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
mkdir -p "$SUBS"
|
||||||
|
|
||||||
|
case ${1#-} in
|
||||||
|
h) usage ;;
|
||||||
|
g) gen_links ;;
|
||||||
|
u) update_subs ;;
|
||||||
|
c) cat_subs ;;
|
||||||
|
d) daemonize ;;
|
||||||
|
*) get_sel ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
Loading…
Reference in New Issue
Block a user