summaryrefslogtreecommitdiff
path: root/bin/menuscripts
diff options
context:
space:
mode:
Diffstat (limited to 'bin/menuscripts')
-rwxr-xr-xbin/menuscripts/aumount97
-rwxr-xr-xbin/menuscripts/commander261
-rwxr-xr-xbin/menuscripts/ddl88
-rwxr-xr-xbin/menuscripts/keyadd62
-rwxr-xr-xbin/menuscripts/mmedia59
-rwxr-xr-xbin/menuscripts/mpass27
-rwxr-xr-xbin/menuscripts/mplay76
-rwxr-xr-xbin/menuscripts/muzrname22
-rwxr-xr-xbin/menuscripts/pomo78
-rwxr-xr-xbin/menuscripts/shdul.sh157
-rwxr-xr-xbin/menuscripts/td65
-rwxr-xr-xbin/menuscripts/treetag.sh128
-rwxr-xr-xbin/menuscripts/tsh204
13 files changed, 1324 insertions, 0 deletions
diff --git a/bin/menuscripts/aumount b/bin/menuscripts/aumount
new file mode 100755
index 0000000..8a2b785
--- /dev/null
+++ b/bin/menuscripts/aumount
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+tmp="$(mktemp)"
+test "$(id -u)" != "0" && sudo="sudo"
+
+logn () { >&2 printf "%s\n" "$@"; }
+log () { >&2 printf '%s' "$@"; }
+
+# Read one character
+read_char ()
+{
+ log ">"
+ old_stty_cfg=$(stty -g)
+ stty raw
+ dd ibs=1 count=1 2> /dev/null
+ stty "$old_stty_cfg"
+ logn ""
+}
+
+get_dev () { grep "^$1\." "$tmp" | cut -f 2- -d ' '; }
+
+# mount the device with $1 as the choice
+mount ()
+{
+ dev="$(get_dev "$1")"
+ test -z "$dev" && exit 1
+
+ logn "Mounting /dev/$dev on /media/$dev"
+ mkdir -p /media/"$dev"
+ $sudo mount /dev/"$dev" /media/"$dev" > /dev/null 2>&1 ||
+ return 1
+}
+
+# umount the device with $1 as the choice
+umount ()
+{
+ mountpoint="$(sed -n "${1}p" "$tmp" | awk '{print $3}')"
+ test -z "$mountpoint" && exit 1
+
+ logn "Unmounting $mountpoint"
+ $sudo umount "$mountpoint" ||
+ return 1
+}
+
+ejekt ()
+{
+ dev="$(get_dev "$1" | sed 's/.$//')"
+ test -z "$dev" && exit 1
+
+ logn "Ejecting /dev/$dev"
+ $sudo eject /dev/"$dev" > /dev/null 2>&1 ||
+ return 1
+}
+
+# print lsblk, use $1 to print only devices with mountpoints or without
+pr_lsblk ()
+{
+ clear
+ lsblk -o name,size,type,mountpoint
+ logn "───────────────────────────────────"
+ lsblk --ascii -o name,mountpoint |
+ grep '^.-' |
+ while read -r line
+ do
+ words="$(printf "%s" "$line" | wc -w)"
+ test "$words" -eq "${1:-1}" && continue
+ printf "%s\n" "$line"
+ done |
+ cut -f 2- -d "-" |
+ awk '{print NR ". " $0}' |
+ tee "$tmp" >&2
+}
+
+cleanup () { rm -f "$tmp"; }
+
+trap cleanup EXIT INT
+
+logn "m(ount) u(mount) (e)ject ?"
+choice="$(read_char)"
+
+case $choice in
+ "m") i=2; cmd=mount ;;
+ "u") i=1; cmd=umount ;;
+ "e") i=2; cmd=ejekt ;;
+ *) exit 1 ;;
+esac
+
+pr_lsblk "$i"
+choice="$(read_char)"
+printf "%s" "$choice" | grep -q "[0-9]" || exit 1
+
+if $cmd "$choice"
+then
+ logn "Successful."
+else
+ logn "Failed."
+fi
diff --git a/bin/menuscripts/commander b/bin/menuscripts/commander
new file mode 100755
index 0000000..3f26429
--- /dev/null
+++ b/bin/menuscripts/commander
@@ -0,0 +1,261 @@
+#!/bin/sh
+
+# An attempt to unify usage of multiple launchers.
+# MENUCMD must be set to the name of the lancher
+#
+# look at the last line to understand how it works
+
+### TODO
+# instead of dynamic, provide a command to set width and height manually
+
+tmp="$(mktemp -u)"
+export menuopts=""
+TOFIRC="$HOME/.config/tofi/config"
+
+options='
+horizontal
+height
+center
+input
+instant
+long
+prompt
+numbered
+prefix
+width'
+
+cleanup() { rm -f "$tmp"; }
+
+logn() { >&2 printf '%s\n' "$@"; }
+die() { logn "$@"; exit 1; }
+
+help() {
+ >&2 cat <<-EOF
+ Usage: commander OPTIONS...
+
+ An attempt to regroup multiple launchers into one,
+ it detects the value of the MENUCMD variable
+
+ try:
+ MENUCMD=tofi commander < /etc/passwd
+
+ Options:
+ -h Display items horizontally
+ -c Display centered
+ -i Take an input and return it to stdout
+ -p ARG Prompt user with the specified argument
+ -l Display in long list format
+ -n Output the number of matches
+ -r Select an executable from PATH
+ -1 Invert instant select on single match
+ -y ARG Use specified height
+ -w ARG Use specified width
+ -x Invert prefix matching
+ EOF
+}
+
+add_option() { menuopts="$menuopts $*"; }
+get_prop() { awk -F '=' "/$1 *=*/ {print \$2}" "$TOFIRC"; }
+
+
+trap cleanup EXIT
+
+# Helper functions
+case "$MENUCMD" in
+ tofi)
+ ### Get a height based on a number of items
+ # $1: max items count
+ get_height()
+ {
+ font_size="$(get_prop "font-size")"
+ padding_top="$(get_prop "padding-top")"
+ padding_bottom="$(get_prop "padding-bottom")"
+
+ printf "%s" "$((
+ font_size*2 + padding_top + padding_bottom + 2 +
+ ($1)*(font_size*2 - 2)
+ ))"
+
+ }
+ ### Get a width based on a maximum character length
+ # $1: max character length
+ get_width()
+ {
+ font_size="$(get_prop "font-size")"
+ padding_left="$(get_prop "padding-left")"
+ padding_right="$(get_prop "padding-right")"
+ printf "%s" "$(($1*(font_size-2) + 3 + padding_left + padding_right))"
+ } ;;
+ *) ;;
+esac
+
+
+main()
+{
+ # shellcheck disable=SC2317
+ case "$MENUCMD" in
+ tofi)
+ center() { add_option "--anchor=center --width=252"; }
+
+ horizontal() {
+ add_option "--horizontal=true --height=32"
+ add_option "--result-spacing=12"
+ add_option "--selection-background=#88c0d0"
+ add_option "--selection-background-padding=6,4"
+ add_option "--padding-top=0 --padding-bottom=0"
+ add_option "--margin-top=4"
+ }
+
+ long() { add_option "--width=100% --height=$(get_height 20)"; }
+
+ input() {
+ add_option "--height=$(get_height 0) --width=$(get_width 24)"
+ pre_filter() { cat /dev/null; }
+ }
+
+ prefix() { add_option '--matching-algorithm=normal'; }
+
+ instant() { add_option "--auto-accept-single=false"; }
+
+ prompt() { add_option '--prompt-text' "$prompt_arg"; }
+
+ numbered() {
+ pre_filter() { awk '{print NR, $0}'; }
+ post_filter() { awk '{print $1}'; }
+ }
+
+ height() { add_option "--height=$(get_height "$height_arg")"; }
+ width() { add_option "--width=$(get_width "$width_arg")"; }
+
+ run() { tofi-run $*; }
+
+ menucmd() { tofi "$@"; } ;;
+
+ dmenu)
+ horizontal() { add_option "-l 0"; }
+
+ center() { add_option "-c"; }
+
+ long() { add_option "-l 20 -g 1"; }
+
+ input() {
+ pre_filter() { cat /dev/null; }
+ }
+
+ instant() { add_option "-n"; }
+
+ prefix() { add_option '-x -i'; }
+
+ prompt() { add_option '-p' "$prompt_arg"; }
+
+ numbered() { add_option "-px"; }
+
+
+ height() { add_option "-l $height_arg"; }
+ width() { add_option "-g $width_arg"; }
+
+ run() { dmenu_path | dmenu $*; }
+
+ menucmd() { dmenu $*; } ;;
+
+ fzf)
+ center() { :; }
+ horizontal() { :; }
+ long() { :; }
+ height() { :; }
+ width() { :; }
+ instant() { :; }
+ prefix() { :; }
+
+ input() {
+ pre_filter() { cat /dev/null; }
+ add_option '--print-query'
+ }
+
+ prompt() { add_option "--prompt" "$prompt_arg"; }
+
+ numbered() {
+ pre_filter() { awk '{print NR, $0}'; }
+ add_option '--with-nth' '2..'
+ post_filter() { awk '{print $1}'; }
+ }
+
+ run() {
+ IFS=:
+ set -f
+ find -L $PATH -type f -printf "%f\n" 2> /dev/null | sort -u | fzf $*
+ }
+
+ menucmd() { fzf -0 -1 "$@"; } ;;
+
+ *) ;;
+ esac
+
+ for option in $options
+ do eval "$option=0"
+ done
+
+ while getopts ":cdhilop:nr1w:xy:v" opt
+ do
+ # shellcheck disable=SC2034
+ case $opt in
+
+ # display horizontal
+ h) horizontal=1 ;;
+ # display centered
+ c) center=1 ;;
+ # take an input and return it on stdout
+ i) input=1 ;;
+ # adjust size and style dynamically
+ p) prompt=1; prompt_arg="$OPTARG";;
+ # long list format
+ l) long=1 ;;
+ # output number of match
+ n) numbered=1 ;;
+ # select an executable from PATH
+ r) run=1 ;;
+ # invert instant select on single match
+ 1) instant=1 ;;
+ w) width=1; width_arg="$OPTARG" ;;
+ # invert prefix matching
+ x) prefix=1 ;;
+ y) height=1; height_arg="$OPTARG" ;;
+
+ :) die "Option '-$OPTARG' requires an argument" ;;
+ ?) die "Invalid option: -$OPTARG" ;;
+ esac
+ done
+
+ # quit if stdin is empty
+ [ -t 0 ] && [ "${run:-0}" -eq 0 ] && [ "$input" -eq 0 ] && help && exit 1
+
+ # call options
+ for option in $options
+ do eval "test \"\$$option\" -eq 1" && $option
+ done
+
+ if [ "$run" ]
+ then
+ run "$menuopts"
+ exit
+ fi
+
+ command -v pre_filter > /dev/null || pre_filter() { cat; }
+ command -v post_filter > /dev/null || post_filter() { cat; }
+
+ ! which "${MENUCMD:-fzf}" > /dev/null && die "MENUCMD '$MENUCMD' was not found."
+
+ if command -v pre_cmd > /dev/null
+ then
+ pre_cmd > "$tmp"
+ logn "menuopts: $menuopts"
+ # shellcheck disable=SC2086
+ pre_filter < "$tmp" | menucmd $menuopts | post_filter
+ else
+ logn "menuopts: $menuopts"
+ # shellcheck disable=SC2086
+ pre_filter | menucmd $menuopts | post_filter
+ fi
+}
+
+main "$@"
diff --git a/bin/menuscripts/ddl b/bin/menuscripts/ddl
new file mode 100755
index 0000000..dd10af5
--- /dev/null
+++ b/bin/menuscripts/ddl
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+# colors
+red="$(printf '\033[31m')"
+blue="$(printf '\033[34m')"
+cyan_light="$(printf '\033[36m')"
+yellow="$(printf '\033[33m')"
+green="$(printf '\033[32m')"
+italic="$(printf '\033[3m')"
+reset="$(printf '\033[0m')"
+
+deadlines="$HOME/docs/filios"/deadlines
+
+die() { >&2 printf '%s\n' "$*"; exit 1; }
+usage()
+{
+ cat <<EOF
+usage: ddl [COMMAND] [ARGUMENT]
+ ddl lists deadlines
+COMMANDS
+ new TEXT... add a new deadline
+ coming [NUM] show the nearest coming deadline(s)
+ delete NUM delete deadline by number
+ grep REGEX show deadlines matching regex
+ help show usage
+ edit edit file in \$EDITOR
+EOF
+ exit 1
+}
+wrong_usage() { >&2 printf 'Wrong usage!\n'; usage; }
+
+colorize()
+{
+ sed \
+ -e "s/^#.*/${red}&${reset}/" \
+ -e "s/^\s*[-?!*].*/${cyan_light}&${reset}/" \
+ -e "s/([^()]\+)/${yellow}${italic}&${reset}/" \
+ -e "s/^\s*#.*/${blue}${italic}&${reset}/"
+}
+list_deadlines() { cat "$deadlines"; }
+# list dates in chronological order
+list_dates()
+{
+ grep '^#' "$deadlines" |
+ sort -t '/' -k 3 -k 2 -k 1 -n |
+ uniq |
+ sed 's@/@.@g'
+}
+
+
+# shellcheck disable=SC2142
+
+### MAIN
+
+# Arguments without an option
+case "$1" in
+ h*) usage ;;
+
+ e*) $EDITOR "$deadlines" ;;
+
+ c*)
+ i=1
+ list_dates | head -n "${2:-1}" |
+ while read -r date
+ do
+ sed -n "/$date/,/^#\|^$/p" "$deadlines" |
+ colorize |
+ sed "1s/.*/& ($green$i$reset)/"
+ i=$((i+1))
+ done ;;
+
+ "") list_deadlines | colorize ;;
+
+ *) false ;;
+
+esac && exit
+
+arg="$1"
+shift
+[ "$1" ] || wrong_usage
+
+case "$arg" in
+
+ d*) sed -i "/$(list_dates | sed -n "${1}p")/,/^$/d" "$deadlines" ;;
+
+ *) wrong_usage ;;
+
+esac
diff --git a/bin/menuscripts/keyadd b/bin/menuscripts/keyadd
new file mode 100755
index 0000000..12519ec
--- /dev/null
+++ b/bin/menuscripts/keyadd
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+log()
+{
+ notify-send -t 1000 "keyadd" "$1"
+ >&2 printf '%s\n' "$1"
+}
+
+SSHFOLDER="$HOME/.ssh"
+
+# Test if can connect to ssh-agent
+ssh-add -l > /dev/null 2>&1
+if [ $? -gt 1 ] # ignore if there are no identities
+then
+ log "Could not connect to agent."
+ exit 1
+fi
+
+delete=0
+if [ "$1" = "-d" ]
+then
+ delete=1
+ shift
+fi
+
+if [ "$1" ]
+then
+ key_pretty="$1"
+else
+ key_pretty="$(find "$SSHFOLDER" -iname "*.pub" |
+ sed "s@$SSHFOLDER/\(.*\)\.pub\$@\1@" |
+ commander -xc)"
+fi
+[ "$key_pretty" ] || exit 1
+key="$SSHFOLDER/$key_pretty"
+
+if [ ! -f "$key" ]
+then
+ log "No key found at: $key"
+ exit 1
+fi
+
+if [ "$delete" -eq 1 ]
+then
+ ssh-add -q -d - < "$key".pub 2> /dev/null &&
+ log "Deleted $key_pretty." ||
+ log "Could not delete."
+ exit
+fi
+
+# check if key is already added
+if ssh-add -l | grep -q "$(ssh-keygen -lf "$key")"
+then
+ log "Key already added."
+ exit 1
+fi
+
+HOST=$(hostnamectl hostname)
+export PASSWORD="keys/$HOST/ssh/$key_pretty"
+export SSH_ASKPASS="sshpass"
+ssh-add -q - < "$key" &&
+ log "Added $key_pretty."
diff --git a/bin/menuscripts/mmedia b/bin/menuscripts/mmedia
new file mode 100755
index 0000000..63d3f7b
--- /dev/null
+++ b/bin/menuscripts/mmedia
@@ -0,0 +1,59 @@
+#!/usr/bin/env sh
+
+tmp="/tmp/mmedia"
+
+dirs="$HOME/dl"
+
+concat_path() { sed "s#^$HOME#\~#;s#\([^/]\)[^/]*/#\1/#g"; }
+
+case "$1" in
+ "video")
+ regex='^.\+\.\(webm\|mp4\|mpeg\|mkv\)$'
+ dirs="$dirs $HOME/vids"
+ viewer() { $PLAYER "$1"; } ;;
+ "pdf")
+ regex='^.\+\.\(pdf\)$'
+ dirs="$dirs $HOME/docs"
+ viewer() { $VIEWER "$1"; } ;;
+ "image")
+ regex='^.\+\.\(png\|avif\|jpg\)$'
+ dirs="$dirs $HOME/pics"
+ viewer() { $IMAGE "$1"; } ;;
+ "cursus")
+ regex='^.*/[cC]ursus/index.html$'
+ dirs="$HOME/docs/school/Vakken"
+ concat_path() { sed "s#$dirs/##;s#/[cC]ursus/index.html##"; }
+ viewer() { $BROWSER "$1"; } ;;
+ "schoolpdf")
+ regex='^.\+\.\(pdf\)$'
+ dirs="$HOME/docs/school/Vakken"
+ concat_path() { sed "s#$dirs/##;s#/[cC]ursus/index.html##;s#/Cursus/viewer/files/#: #"; }
+ viewer() { $VIEWER "$1"; } ;;
+ *)
+ choice="$(cat <<-EOF | dmenu -g 5 -l 1 -c
+ video
+ pdf
+ image
+ cursus
+ schoolpdf
+ EOF
+ )"
+ [ "$choice" ] || exit 1
+ "$0" "$choice"
+ exit ;;
+esac
+
+shift
+[ "$1" ] && dirs="$1"
+
+choice="$(
+ find -L $dirs 2> /dev/null |
+ grep "$regex" |
+ sort | tee "$tmp" |
+ concat_path |
+ dmenu -px -c -n -x -l 10 -g 1 -F)"
+
+file="$(sed -n "${choice}p" "$tmp")"
+[ -r "$file" ] || exit 1
+
+viewer "$file"
diff --git a/bin/menuscripts/mpass b/bin/menuscripts/mpass
new file mode 100755
index 0000000..7029ffe
--- /dev/null
+++ b/bin/menuscripts/mpass
@@ -0,0 +1,27 @@
+#!/usr/bin/env sh
+store="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
+
+# list passwords, group directories first
+list_pswds()
+{
+ find "$1" \
+ -maxdepth 1 -mindepth 1 \
+ -not -name '.*' -type d -printf "%y\t%f\n" -o \
+ -not -name '.*' -not -type d -printf "%y\t%f\n" |
+ sort -k1 -k2 |
+ cut -f 2 | sed 's/\.gpg$//'
+}
+
+while [ -d "$store/$file" ]
+do
+ choice="$(list_pswds "$store/$file" | commander -c)"
+ [ "$choice" ] || exit 1
+ [ -z "$file" ] && file="$choice" || file="$file/$choice"
+done
+[ "$file" ] || exit 1
+
+pass show -c "$file" || exit 1
+notify-send -t 1000 "mpass" "copied <b>$file</b>"
+
+[ "$WAYLAND_DISPLAY" ] && cliphist list >/dev/null && # on wayland and cliphist is running
+ cliphist list | head -n 1 | cliphist delete
diff --git a/bin/menuscripts/mplay b/bin/menuscripts/mplay
new file mode 100755
index 0000000..57af631
--- /dev/null
+++ b/bin/menuscripts/mplay
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+MUSIC="$HOME/music"
+
+
+list_dirs()
+{
+ find -L "$1" \
+ -mindepth 1 -maxdepth 1 \
+ -not -name "*.cue" |
+ sed "s@^$1/@@" | sort;
+}
+
+find_song()
+{
+ file="$MUSIC"
+ while [ -d "$file" ]
+ do
+ choice="$(list_dirs "$file" | commander -clx)"
+ [ "$choice" ] || return
+ file="$file/$choice"
+ done
+ printf '%s' "${file##"$MUSIC/"}"
+}
+
+play_song()
+{
+ printf '%s\n' "$1" | tr '\n' '\0' |
+ xargs -0I{} mpc insert "{}" || return
+ mpc next 2> /dev/null
+ mpc play 2> /dev/null
+}
+
+main()
+{
+ choice="$(printf 'volume\nsearch\ncommand\nall_search\nquit' | commander -c -w 5 -y 1)"
+ case "$choice" in
+ volume)
+ # Change volume while no error
+ while true
+ do
+ volume="$(mpc volume | awk -F '[ %]' '{print $2}')"
+ nb="$(printf 'p\nm' | commander -c -y 2 -p "$volume" | tr 'pm' '+-')"
+ [ "$nb" ] || break
+ change="$(commander -ci -p "$volume$nb")"
+ mpc volume "$nb$change" || break
+ done ;;
+
+ search)
+ song="$(find_song)"
+ [ "$song" ] || return
+
+ choice="$(printf 'insert\nadd\nplay' | commander -c -w 3 -y 1)"
+ case "$choice" in
+ insert|add) mpc "$choice" "$song" ;;
+ play) play_song "$song" ;;
+ esac ;;
+
+ command)
+ mpc "$(printf 'next\nprevious\nclear\nstop\ntoggle' |
+ commander -c -w 8 -y 5)" ;;
+
+ all_search)
+ song="$(mpc listall | commander -cxh)"
+ [ "$song" ] || return
+ play_song "$song";;
+ quit) return 1 ;;
+
+ *) return 1 ;;
+ esac
+ return 0
+}
+
+while main
+do :;
+done
diff --git a/bin/menuscripts/muzrname b/bin/menuscripts/muzrname
new file mode 100755
index 0000000..3f63aff
--- /dev/null
+++ b/bin/menuscripts/muzrname
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+rename()
+{
+ ## Expression 1:
+ # For CDs or volumes 1-01 -> 1_01
+ ## Expression 2:
+ # * don't match the start (^)
+ # match the first part with a number and replace following [ -.] by '. '
+ sed -e 's/^\s*\([0-9]\+\)[ -_.]\([0-9]\+\)/\1_\2. /' \
+ -e 's#\s*\([0-9]\+\)[ -.]\+#\1. #'
+}
+
+if [ ! -t 0 ]
+then
+ rename
+elif [ "$1" ]
+then
+ printf '%s' "$1" | rename
+else
+ exit 1
+fi
diff --git a/bin/menuscripts/pomo b/bin/menuscripts/pomo
new file mode 100755
index 0000000..e8795e7
--- /dev/null
+++ b/bin/menuscripts/pomo
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+### FUNCTIONS
+notif() {
+ herbe "pomo" "$1" &
+ sleep 1
+ pkill -SIGUSR1 herbe
+}
+logn () { >&2 printf '%s\n' "$@"; }
+log () { >&2 printf '%s' "$@"; }
+
+# Plays ringing sound
+# Then wait for user input to start/end the break
+player_command()
+{
+ >&2 printf ' > '
+ [ -r "$ringSound" ] || PLAYER=""
+ case "$PLAYER" in
+ mpv) mpv --loop --msg-level=all=no "$ringSound" ;;
+ "") head -n 1;;
+ *) $PLAYER "$ringSound" ;;
+ esac
+}
+
+# $1: time in minutes
+# $2: msg for notification
+ring_ring()
+{
+ logn "$(date '+%R') B $round"
+ player_command
+ herbe "pomo" "$2" &
+ sleep "${1}m"
+ player_command
+ >&2 printf '\n'
+}
+
+### PROGRAM
+main()
+{
+ round="${1-0}"
+ case "$round" in
+ 0|1|2|3) ;;
+ ring) ;;
+ *) logn "Invalid \$round value." && exit 1 ;;
+ esac
+
+ ringSound="${XDG_DATA_HOME:-$HOME/.local/share}"/pomo/ring.aac
+ [ -r "$ringSound" ] && logn "ring: $ringSound"
+
+ if [ "$1" = "ring" ]
+ then
+ player_command
+ exit
+ fi
+
+ # Countdown
+ for msg in "three" "two" "one"
+ do notif "$msg"
+ done
+
+ while true
+ do
+ notif "START"
+ logn "$(date '+%R') S $round"
+ sleep 25m
+ if [ "$round" -eq 3 ]
+ then
+ ring_ring 20 "GIGA BREAK TIME"
+ round=0
+ else
+ ring_ring 5 "BREAK TIME"
+ round=$((round+1))
+ fi
+ done
+}
+
+trap 'exit 1' INT
+main "$@"
diff --git a/bin/menuscripts/shdul.sh b/bin/menuscripts/shdul.sh
new file mode 100755
index 0000000..4e1d744
--- /dev/null
+++ b/bin/menuscripts/shdul.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+
+tmp="$(mktemp)"
+
+red="$(printf '\033[35m')"
+blue="$(printf '\033[34m')"
+reset="$(printf '\033[0m')"
+underline="$(printf '\033[4m')"
+bold="$(printf '\033[1m')"
+
+THISDIR="$(dirname "$(readlink -f "$0")")"
+# schedule file
+SCHEDULE="$THISDIR/schedule"
+# blocks directory
+BLOCKS="$THISDIR/blocks"
+
+die () { >&2 printf "%s" "$@"; exit 1; }
+log () { >&2 printf '%s' "$@"; }
+logn () { >&2 printf '%s\n' "$@"; }
+
+cleanup () { rm -f "$tmp"; }
+
+trap cleanup EXIT
+
+read_char ()
+{
+ old_stty_cfg=$(stty -g)
+ stty raw -echo
+ dd ibs=1 count=1 2> /dev/null
+ stty "$old_stty_cfg"
+}
+
+edit_schedule ()
+{
+ while true
+ do
+ char="$(read_char)"
+ case "$char" in
+ q) exit ;;
+ a) add_block ;;
+ # new or from save
+ d) delete_block ;;
+ '') ;;
+ *) printf "%s" "$char"
+ esac
+ done
+
+}
+
+# adds NOW to the schedule and gets its line number
+# $1: schedule file
+get_now ()
+{
+ fake="$(date +%R)\tZZZZZZZZZZZZZZZZ - NOW"
+ sed "\$a $fake" "$1" |
+ sort -g |
+ awk "/^$fake/ {print NR}"
+}
+
+# prints schedule in a nice format
+# $1: schedule file
+# $2: blocks dir
+print_schedule ()
+{
+ now="$(get_now "$1")"
+
+ clear
+
+ i=1
+ while read -r line
+ do
+ [ "$i" -lt "$((now-1))" ] && i=$((i+1)) && continue
+ i=$((i+1))
+
+ # Colors
+ if [ "$i" -lt "$now" ]
+ then
+ printf "%s" "${reset}${red}"
+ elif [ "$i" -eq "$now" ]
+ then
+ printf "%s" "${reset}${bold}"
+ else
+ printf "%s" "${reset}${blue}"
+ fi
+
+ block_file="$2/$(printf "%s" "$line" | cut -f2)"
+ block_time="$(printf "%s" "$line" | cut -f1)"
+
+ # markup
+ printf "%s\n" "$block_time"
+ sed 's/.*/│&/' "$block_file"
+ # printf '\n'
+
+ done < "$1"
+
+ printf '%s' "${reset}"
+}
+
+# $1: schedule file
+# $2: blocks dir
+view_schedule()
+{
+ trap "break" INT
+
+ prev_now="$(get_now "$1")"
+ print_schedule "$1" "$2"
+ while true
+ do
+ now="$(get_now "$1")"
+
+ # Refresh when new block
+ if [ "$prev_now" -ne "$now" ]
+ then
+ print_schedule "$1" "$2"
+ prev_now="$now"
+ notify-send -u critical -t 5000 "shdul" "$(awk "NR==$((now-1)) {print \$2}" "$1")"
+
+ # Align with clock
+ sleep "$((60-$(date +%-S)))s"
+ else
+ sleep 1m
+ fi
+ done
+
+ clear
+}
+
+main ()
+{
+ echo $$ > "$THISDIR/.scheduler.pid"
+
+ view_schedule "$SCHEDULE" "$BLOCKS"
+ while true
+ do
+ printf ':'
+ char="$(read_char)"
+ case "$char" in
+ h) cat <<-EOF
+
+ h help
+ v view schedule
+ s edit schedule mode
+ q exit
+
+ EOF
+ ;;
+ l) clear ;;
+ v) view_schedule "$SCHEDULE" "$BLOCKS" ;;
+ s) edit_schedule ;;
+ q) exit ;;
+ '') ;;
+ *) printf "%s" "$char"
+ esac
+ done
+}
+
+main
diff --git a/bin/menuscripts/td b/bin/menuscripts/td
new file mode 100755
index 0000000..e905d9b
--- /dev/null
+++ b/bin/menuscripts/td
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+todo="$HOME/docs/filios"/todo
+
+die() { >&2 printf '%s\n' "$*"; exit 1; }
+usage()
+{
+ cat <<EOF
+usage: td [COMMAND] [ARGUMENT]
+ td lists to-do's
+COMMANDS
+ new TEXT... add a new to-do
+ clear remove all to-do's
+ delete NUMBER delete a to-do by number
+ grep REGEX show to-do's matching regex
+ help show usage
+ move NUMBER NUMBER change priority of a to-do
+ edit edit file in \$EDITOR
+EOF
+ exit 1
+}
+wrong_usage() { >&2 printf 'Wrong usage!\n'; usage; }
+
+list_todos() {
+ [ ! -r "$todo" ] && die "No to-do's."
+ [ "$(wc -l < "$todo")" -eq 0 ] && die "No to-do's."
+ awk '{print NR ": " $0}' "$todo"
+}
+
+# shellcheck disable=SC2142
+
+### MAIN
+
+# Arguments without an option
+case "$1" in
+ c*) rm -f "$todo"; >&2 printf 'Cleared.\n' ;;
+ h*) usage ;;
+ e*) $EDITOR "$todo" ;;
+ "") list_todos ;;
+ *) false ;;
+esac && exit
+
+
+arg="$1"
+shift
+[ "$1" ] || wrong_usage
+
+case "$arg" in
+ n*)
+ printf '%s\n' "$*" >> "$todo"
+ list_todos ;;
+
+ d*)
+ sed -i -n "$1!p" "$todo"
+ list_todos ;;
+ g*) list_todos | grep "$*" ;;
+
+ m*)
+ [ "$2" ] || wrong_usage
+ line="$(list_todos | sed "/^$1:/!d;s/^$1: //")"
+ sed -i "${1}d;${2}i${line}" "$todo"
+ list_todos ;;
+
+ *) wrong_usage ;;
+esac
diff --git a/bin/menuscripts/treetag.sh b/bin/menuscripts/treetag.sh
new file mode 100755
index 0000000..3ded637
--- /dev/null
+++ b/bin/menuscripts/treetag.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+VERSION="0.3"
+
+batch=0
+rename=0
+
+help ()
+{
+ cat <<-EOF >&2
+ Usage: treetag.sh [options]
+ Options:
+ -b Enable batch mode
+ -d <directory> Specify the music directory
+ -h Show this help message and exit
+ -r also rename file
+ -v Print version information and exit
+ EOF
+}
+
+log () { >&2 printf '%s\n' "$@"; }
+die () { log "$@"; exit 1; }
+require () { command -v "$1" > /dev/null || die "E: This script needs '$1' to be installed."; }
+ls_dirs() { find . -mindepth 1 -maxdepth 1 -type d -printf "%f\n"; }
+
+### Tag files in the current directory
+# $1: artist name
+# $2: album name
+treetag ()
+{
+ [ -z "$1" ] || [ -z "$2" ] && return 1
+ artist="$1"
+ album="$2"
+
+ >&2 printf "artist: %s\n" "$artist"
+ >&2 printf "album: %s\n" "$album"
+ find . -maxdepth 1 -type f -printf '%f\n' | sort -g |
+ while read -r file
+ do
+ >&2 printf "file: %s\n" "$file"
+ ! soxi "$file" > /dev/null 2>&1 && continue
+
+ # Remove number prefix and extension
+ name="$(printf '%s' "${file%.*}" | sed 's/^[0-9]*\s*[. -]\s*//')"
+ i=$((i+1))
+
+ log "I: [$artist] ($album) #$i $name"
+
+ id3v2 \
+ -a "$artist" \
+ -A "$album" \
+ -t "$name" \
+ -T "$i" \
+ -- "$file"
+ printf '%s\n' "$i" > .count
+
+ [ $rename -eq 1 ] && mv -- "$file" "$i. $name.${file##*.}"
+
+ done
+ if [ -f .count ]
+ then
+ log "I: $(cat .count) file(s) tagged."
+ rm .count
+ else
+ log "I: No files tagged."
+ fi
+}
+
+batch_tag ()
+{
+ artist="${PWD##*/}" # basename of current dir
+ ls_dirs |
+ while read -r album
+ do (cd "$album" && treetag "$artist" "$album")
+ done
+}
+
+# Tag interactively with fzf
+interactive ()
+{
+ require "fzf"
+
+ artist="$(ls_dirs | fzf)"
+ [ "$artist" ] && cd "$artist" || exit 1
+
+ choice="$artist"
+ while true
+ do
+ choice="$(ls_dirs | fzf --prompt "$choice:")"
+ if [ "$choice" ]
+ then
+ album="$choice"
+ cd "$album" || exit 1
+
+ printf 'stop? '
+ head -n 1 | grep -q "[yY]" && break
+ else
+ break
+ fi
+ done
+
+ treetag "$artist" "$album"
+}
+
+while getopts ":d:bhrv" opt
+do
+ case $opt in
+ b) batch=1 ;;
+ d) musdir="$OPTARG" ;;
+ h) help; exit ;;
+ r) rename=1 ;;
+ v) log "treetag.sh $VERSION"; exit ;;
+ :) die "E: Option -$OPTARG requires an argument" ;;
+ ?) die "E: Invalid option: -$OPTARG" ;;
+ esac
+done
+
+require "id3v2"
+require "sox"
+
+cd "${musdir:=.}" || exit 1
+
+if [ $batch -eq 1 ]
+then
+ batch_tag
+else
+ interactive
+fi
diff --git a/bin/menuscripts/tsh b/bin/menuscripts/tsh
new file mode 100755
index 0000000..eac0c3e
--- /dev/null
+++ b/bin/menuscripts/tsh
@@ -0,0 +1,204 @@
+#!/bin/sh
+
+PROG="$(basename "$0")"
+## VARIABLES
+# copy command and deps variable
+deps="pup curl $MENUCMD"
+
+LIBPFX=/home/aluc/.local/share/tsh
+module='1337x.sh' # default module
+
+# Files
+export tmp="/tmp/$PROG"
+files="seeds sizes names html tmp_types"
+# Use export so that these variables can be used inside of modules
+for file in $files
+do eval "export $file=$tmp/$file"
+done
+# Files not in $files won't be deleted
+export results="$tmp/results"
+export links="$tmp/links"
+
+types="music anime movies shows other software games isos books"
+
+if [ "$WAYLAND_DISPLAY" ]
+then
+ clipp() { wl-copy -n; }
+ deps="$deps wl-copy"
+else
+ clipp() { xclip -selection clipboard -r; }
+ deps="$deps xclip"
+fi
+## FUNCTIONS
+
+help ()
+{
+ >&2 cat <<-EOF
+ Usage: $PROG [options] query
+ Options:
+ -h Show this help message and exit
+ -r Skip getting pages and use last results
+ -s OPTION Sort results based on the specified OPTION
+ Available options: seeds, size, name
+ -m MODULE Select a module, if MODULE is 'list',
+ lists out available modules
+ EOF
+}
+
+log () { >&2 printf '%s' "$*"; }
+logn () { >&2 printf '%s\n' "$*"; }
+die () { logn "$@"; exit 1; }
+
+confirm()
+{
+ log "$1"
+ head -n 1 | grep -q "[yY]"
+}
+
+dependencies ()
+{
+ for p in $deps
+ do
+ if ! command -v "$p" > /dev/null
+ then
+ logn "E: Program '$p' not found."
+ error=1
+ fi
+ done
+ [ "${error:-0}" -eq 1 ] && exit 1
+}
+
+# shellcheck disable=SC2046
+# (we use word splitting on purpose)
+isOnline () { grep -q '1' $(echo /sys/class/net/*/carrier | sed 's#/sys/class/net/lo/carrier ##'); }
+
+# Remove temp files and quit
+cleanup ()
+{
+ for file in $files
+ do eval "rm -f \$$file"
+ done
+}
+
+list_modules () { find -L "$LIBPFX" -type f -printf "%f\n"; }
+
+# get a query from user based on MENUCMD
+get_query ()
+{
+ isOnline || exit 1
+ if [ "${query:="$*"}" ]
+ then
+ printf "%s" "$query"
+ else
+ log ' > '
+ head -n 1
+ fi | tr ' ' '+'
+}
+
+# Select a result from the result file sorterd with sort_results
+# and select with fzf
+select_result ()
+{
+ command -v sort_results > /dev/null || sort_results() { sort -k3 -n -r; }
+ awk '{print NR, $0}' "$results" |
+ sort_results |
+ column -t -l 3 |
+ fzf -m --with-nth 2.. |
+ awk '{print $1}'
+}
+
+show_files()
+{
+ hash="${1##*btih:}"
+ hash="${hash%%&*}"
+
+ # Download the torrent file from a torrent website
+ curl -s "https://itorrents.org/torrent/${hash}.torrent" > "$tmp"/.torrent
+ transmission-show "$tmp"/.torrent | sed -n '/^FILES/,$p' | head -n -1 | tail -n +3 >&2
+ rm -f "$tmp"/.torrent
+}
+
+# Select a type after having displayed them with 'show_types'
+select_type()
+{
+ for type in $types
+ do printf "%s\n" "$type"
+ done | fzf
+}
+
+trap "exit 1" INT
+trap "cleanup" EXIT
+
+## OPTIONS
+skip=0
+while getopts ":hm:rs:" opt
+do
+ case $opt in
+ h) help && exit ;;
+ m)
+ [ "$OPTARG" = "list" ] && list_modules && exit
+ module="$(list_modules | grep -m 1 "^$OPTARG")"
+ [ -z "$module" ] && die "No valid module for '$OPTARG'"
+ logn "module: $module" ;;
+ r)
+ [ ! -r "$results" ] && die "No previous results found."
+ skip=1 ;;
+ s)
+ case $OPTARG in
+ "seeds") sort_results() { sort -k3 -n -r; } ;;
+ "size") sort_results() { sort -k2 -h -r; } ;;
+ "name") sort_results() { sort -k4; } ;;
+ *) die "argument '$OPTARG' not seeds,size,name" ;;
+ esac ;;
+ :) die "Option -$OPTARG requires an argument" ;;
+ ?) die "Invalid option: -$OPTARG" ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+dependencies
+
+# Get the torrents with module
+if [ $skip -ne 1 ]
+then
+ mkdir -p "$tmp"
+
+ query="$(get_query "$*")"
+ [ "$query" ] || exit 1
+
+ # Get results
+ rm -f "$results" "$links"
+ # shellcheck source=/usr/local/lib/$PROG/nyaa.sh disable=SC1091
+ . "$LIBPFX/$module"
+ [ -f "$results" ] || die "No results."
+
+ # Save which module was used
+ printf "%s\n" "$module" >> "$links"
+else
+ module="$(tail -n 1 "$links")"
+fi
+
+# acquire get_magnet function
+# shellcheck source=/usr/local/lib/$PROG/nyaa.sh disable=SC1091
+getfunctions=1 . "$LIBPFX/$module"
+
+# select result from "$results"
+for choice in $(select_result | xargs)
+do
+ printf 'choice: %s\n' "$choice"
+ magnet="$(get_magnet "$choice")"
+ [ "$magnet" ] || exit 1
+
+ confirm 'files?' && show_files "$magnet"
+
+ if confirm 'download?'
+ then
+ type="$(select_type)"
+ [ "$type" ] || exit 1
+ transmission-remote debuc.com -a "$magnet" -w "/downloads/$type"
+ elif confirm "copy?"
+ then
+ echo "$magnet" | clipp
+ fi
+
+done