summaryrefslogtreecommitdiff
path: root/home/xyz/.local/bin
diff options
context:
space:
mode:
Diffstat (limited to 'home/xyz/.local/bin')
-rwxr-xr-xhome/xyz/.local/bin/cfg24
-rwxr-xr-xhome/xyz/.local/bin/chmodef5
-rwxr-xr-xhome/xyz/.local/bin/convwall34
-rwxr-xr-xhome/xyz/.local/bin/curlkg42
-rwxr-xr-xhome/xyz/.local/bin/curlncm86
-rwxr-xr-xhome/xyz/.local/bin/curlqb18
-rwxr-xr-xhome/xyz/.local/bin/gita18
-rwxr-xr-xhome/xyz/.local/bin/gitfork8
-rwxr-xr-xhome/xyz/.local/bin/gitpu19
-rwxr-xr-xhome/xyz/.local/bin/mmi27
-rwxr-xr-xhome/xyz/.local/bin/mpva11
-rwxr-xr-xhome/xyz/.local/bin/mpvy41
-rwxr-xr-xhome/xyz/.local/bin/mvtr14
-rwxr-xr-xhome/xyz/.local/bin/o15
-rwxr-xr-xhome/xyz/.local/bin/orgext24
-rwxr-xr-xhome/xyz/.local/bin/reco27
-rwxr-xr-xhome/xyz/.local/bin/sbar90
-rwxr-xr-xhome/xyz/.local/bin/shufwall9
-rwxr-xr-xhome/xyz/.local/bin/time.uuid6
-rwxr-xr-xhome/xyz/.local/bin/topa6
-rwxr-xr-xhome/xyz/.local/bin/upd120
21 files changed, 644 insertions, 0 deletions
diff --git a/home/xyz/.local/bin/cfg b/home/xyz/.local/bin/cfg
new file mode 100755
index 00000000..de40874a
--- /dev/null
+++ b/home/xyz/.local/bin/cfg
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# didn't consider $HOME contain space? need improve?
+while getopts clLsSmM opt; do
+ case $opt in
+ c|l|s) dir="$HOME"
+ cmd="git --git-dir=$dir/.cfg$opt/ --work-tree=$dir"
+ mods="$XDG_CONFIG_HOME/myconf/cfg${opt}_mods";;
+ L|S) dir=/etc/
+ cmd="sudo git --git-dir=$dir/.cfg$opt/ --work-tree=$dir"
+ mods="/etc/myconf/cfg${opt}_mods";;
+ # -rw-r--r-- == binary 110100100 == octal 644
+ # https://stackoverflow.com/a/1796009
+ m) $cmd ls-tree -r --name-only --full-tree HEAD | nawk -v a="$dir/" '{printf("%s%s\n",a,$0)}' | tr '\n' '\0' | xargs -0 ls -ldA -- | grep -v '^l\|^d' | nawk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k){for(i=10;$i;i++)$9=$9" "$i;printf("%0o %s\n",k,$9)}}' > "$mods"
+ exit;;
+ M) while read -r mod path; do
+ sudo chmod -c "$mod" -- "$path"
+ done < "$mods"
+ exit;;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+$cmd "$@"
diff --git a/home/xyz/.local/bin/chmodef b/home/xyz/.local/bin/chmodef
new file mode 100755
index 00000000..be9b2934
--- /dev/null
+++ b/home/xyz/.local/bin/chmodef
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# https://wiki.archlinux.org/title/File_permissions_and_attributes#Numeric_method
+find "$@" -type d -execdir chmod 755 -- '{}' \+
+find "$@" -type f -execdir chmod 644 -- '{}' \+
diff --git a/home/xyz/.local/bin/convwall b/home/xyz/.local/bin/convwall
new file mode 100755
index 00000000..dff9c43b
--- /dev/null
+++ b/home/xyz/.local/bin/convwall
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# target image aspect ratio
+wratio=$SCR_WIDTH
+hratio=$SCR_HEIGHT
+# chop how much from center
+chopnum=99%
+
+# my benchmark shows -ping is much faster than default
+# https://github.com/ImageMagick/ImageMagick/issues/3183#issuecomment-800955241
+width=$(identify -ping -format '%w' "$1")
+height=$(identify -ping -format '%h' "$1")
+
+ratio=$((wratio*100/hratio))
+dim=$((width*100/height))
+
+if [ "$dim" -ne "$ratio" ]; then
+ if [ "$dim" -lt "$ratio" ]; then
+ chopgeom="${chopnum}x0"
+ width=$((height*wratio/hratio))
+ else
+ chopgeom="0x$chopnum"
+ height=$((width*hratio/wratio))
+ fi
+ # may throw error "sort: write failed: 'standard output': Broken pipe \n sort: write error"
+ # caused by head exit (or close stdin) before sort output complete, can ignore
+ # https://stackoverflow.com/questions/46202653/bash-error-in-sort-sort-write-failed-standard-output-broken-pipe
+ bgcolor=$(convert "$1" -gravity center -chop "$chopgeom" -define histogram:unique-colors=true -format %c histogram:info:- | sort -rn | head -n1 | awk \{print\ \$3\} | cut -c1-7)
+ # another approach is to use "$width" or "x$height" for geometry, current approach is more readable
+ convert "$1" -gravity center -background "$bgcolor" -extent "${width}x$height" "$2"
+else
+ echo 'same aspect ratio, no need to convert' >&2
+ exit 1
+fi
diff --git a/home/xyz/.local/bin/curlkg b/home/xyz/.local/bin/curlkg
new file mode 100755
index 00000000..9389e680
--- /dev/null
+++ b/home/xyz/.local/bin/curlkg
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# references:
+# https://blog.csdn.net/qq_41730930/article/details/79327757
+# https://blog.csdn.net/u011112876/article/details/89634505
+# https://github.com/pystardust/ytfzf
+
+homepage_url='https://node.kg.qq.com/cgi/fcgi-bin/kg_ugc_get_homepage'
+music_url='https://node.kg.qq.com/cgi/fcgi-bin/fcg_get_play_url'
+# num seems only 8 and 15 works
+num=15
+tmp_json_file="$(mktemp --suffix=.json)"
+
+while getopts u: opt; do
+ case $opt in
+ u) uid="$OPTARG";;
+ \?) exit 1;;
+ esac
+done
+# for lan lan, uid=649b9e82272a348b
+# for cheng ruan, uid=64949d822c25328c
+[ -z "$uid" ] && echo 'error: must provide share_uid
+ex: curlkg -u 649b9e82272a348b ./' >&2 && exit 1
+shift $((OPTIND-1))
+download_dir="${1:-"$PWD"}"
+[ -d "$download_dir" ] || mkdir -p "$download_dir"
+
+get_json ()
+{
+ curl -s -G --data-urlencode type=get_uinfo -d outCharset=utf-8 -d start="$1" -d num=$num -d share_uid="$uid" "$homepage_url" | sed 's/.*({\(.*\)}).*/{\1}/' >> "$tmp_json_file"
+}
+
+get_json 1
+ugc_total_count=$(jq '.data.ugc_total_count' "$tmp_json_file")
+# can also use while loop with i=$((i+1))
+# the calculation considers both ugc_total_count%num==0 and ugc_total_count%num>0
+for i in $(seq 2 $(((ugc_total_count+num-1)/num))); do
+ get_json "$i"
+done
+
+jq -r '.data.ugclist[]|"'"$music_url"'?shareid=\(.shareid)\n out=\(.title)_\(.time).m4a"' "$tmp_json_file" | aria2c -d "$download_dir" --auto-file-renaming=false --console-log-level=warn -i-
+rm "$tmp_json_file"
diff --git a/home/xyz/.local/bin/curlncm b/home/xyz/.local/bin/curlncm
new file mode 100755
index 00000000..582214ac
--- /dev/null
+++ b/home/xyz/.local/bin/curlncm
@@ -0,0 +1,86 @@
+#!/bin/sh
+# rewrite TianyiShi2001's python script which rewrite NeteaseCloudMusicApi node.js api
+
+# references:
+# https://github.com/ytdl-org/youtube-dl/issues/18051#issuecomment-859964832
+# https://github.com/Binaryify/NeteaseCloudMusicApi
+# info about encrytion and api:
+# https://github.com/metowolf/NeteaseCloudMusicApi/wiki
+# https://github.com/darknessomi/musicbox/wiki
+
+# lan lan, aid=48860966, rid=793052426, example id=1397315179,1817498734
+# cheng ruan, aid=46703185, rid=792968433
+
+# trial and error to get dj_max, don't know why, maybe because it is 2^31 and it is the size limit of whatever type of the variable
+dj_max=2147483647
+dl_urls_tmp="$(mktemp)"
+# printf 'e82ckenh8dichen8' | hexdump -ve '/1 "%02x"'
+key='653832636b656e683864696368656e38'
+url='/api/song/enhance/player/url'
+request_id=$(date +'%s%3N')_$(seq -w 1 1000 | shuf -n1)
+user_agent='Mozilla/5.0 (Linux; U; Android 9; zh-cn; Redmi Note 8 Build/PKQ1.190616.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.141 Mobile Safari/537.36 XiaoMi/MiuiBrowser/12.5.22'
+cookie="$(printf '"appver":"8.0.0","versioncode":"140","buildver":"1623455100","resolution":"1920x1080","__csrf":"","os":"pc","requestId":"%s"' "$request_id")"
+
+die ()
+{
+ echo "error: $*" >&2
+ exit 1
+}
+
+# let a i r mutually exclusive
+# artist, song, djradio id
+flag=0
+while getopts a:i:r: opt; do
+ case $opt in
+ a) [ "$flag" -eq 1 ] && die "options a,i,r are mutually exclusive"
+ aid="$OPTARG"
+ flag=1;;
+ i) [ "$flag" -eq 1 ] && die "options a,i,r are mutually exclusive"
+ ids="$OPTARG"
+ flag=1;;
+ r) [ "$flag" -eq 1 ] && die "options a,i,r are mutually exclusive"
+ rid="$OPTARG"
+ flag=1;;
+ \?) exit 1;;
+ esac
+done
+[ $flag -eq 0 ] && die "please specify at least one option"
+shift $((OPTIND-1))
+[ $# -gt 1 ] && die "directory name should be at the end"
+download_dir="${1:-"$PWD"}"
+[ -d "$download_dir" ] || mkdir -p "$download_dir"
+
+if [ -n "$ids" ]; then
+ # temporary solution, can improve by auto curl names
+ data="$(echo "$ids" | tr ',' '\n' | awk '{printf("%s\t%s\n",$0,"songname")}')"
+else
+ if [ -n "$rid" ]; then
+ data="$(curl -s -G --data-urlencode id="$rid" -d limit="$dj_max" 'https://music.163.com/djradio' | grep 'songlist\|tt f-thide' | sed -e 's/.*songlist-\(.*\)" class.*/\1/g' -e 's/.*title="\(.*\)".*/\1/g' | paste -sd '\t\n' | sort | tr '/' '_')"
+ # can only curl featured 50 songs for the artist, can improve
+ elif [ -n "$aid" ]; then
+ data="$(curl -s -G --data-urlencode id="$aid" 'https://music.163.com/artist' | grep '\[{.*}\]' | sed -e 's/.*>\[{/\[{/' -e 's/}\]<.*/}\]/' | jq -r '.[]|[.id,.name]|@tsv' | sort | tr '/' '_')"
+ fi
+ # awk code not print separator at beginning and end steal from:
+ # https://unix.stackexchange.com/a/581785/459013
+ # not sure awk one-liner or paste way which is better, need benchmark
+ ids="$(echo "$data" | awk -F'\t' '{printf "%s%s",sep,$1;sep=","}')"
+ #ids="$(echo "$data" | awk -F'\t' '{print $1}' | paste -sd ',')"
+fi
+
+# I don't fully understand following several lines of code
+# I rewrite TianyiShi2001's python script, he rewrites NeteaseCloudMusicApi
+text="$(printf '{"ids":"[%s]","br":999000,"header":{%s}}' "$ids" "$cookie")"
+message="nobody${url}use${text}md5forencrypt"
+digest="$(printf '%s' "$message" | openssl dgst -md5 -hex | awk '{print $2}')"
+params="$(printf '%s-36cd479b6b5-%s-36cd479b6b5-%s' "$url" "$text" "$digest")"
+encrypted_params="$(printf '%s' "$params" | openssl enc -aes-128-ecb -K "$key" | hexdump -ve '/1 "%02X"')"
+# curl default user agent header seems not working
+curl -s -A "$user_agent" -d params="$encrypted_params" 'https://interface3.music.163.com/eapi/song/enhance/player/url' | jq -r '.data|sort_by(.id)|.[].url' > "$dl_urls_tmp"
+[ "$(echo "$data" | wc -l)" -ne "$(wc -l < "$dl_urls_tmp")" ] && die "number of download uls doesn't match request"
+
+# can't download some music if live abroad, may need proxy or vpn
+# using user_agent, content-type header, cookie header, referer are not necessary? but feels faster
+# not sure about cookie header format for aria2c, this link shows that it's kinda messy, so I didn't use it
+# https://github.com/aria2/aria2/issues/545#issuecomment-650070869
+echo "$data" | awk -F'\t' '{printf(" out=%s_%s.mp3\n",$2,$1)}' | paste "$dl_urls_tmp" - -d '\n' | aria2c -U "$user_agent" --header='Content-Type: application/x-www-form-urlencoded' --referer='https://music.163.com' -d "$download_dir" --auto-file-renaming=false --console-log-level=warn -i-
+rm "$dl_urls_tmp"
diff --git a/home/xyz/.local/bin/curlqb b/home/xyz/.local/bin/curlqb
new file mode 100755
index 00000000..f98a41c8
--- /dev/null
+++ b/home/xyz/.local/bin/curlqb
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+curl -s 'https://github.com/qbittorrent/search-plugins/wiki/Unofficial-search-plugins' | awk -F'"' '/Plugins for Private sites/{exit} /\.py/{url=$2;getline;getline;getline;if($0 !~ /❗|✖/)print url}' | xargs curl -s -Z -L --remote-name-all --create-dirs --output-dir "$1" --
+
+#curl -s 'https://github.com/qbittorrent/search-plugins/wiki/Unofficial-search-plugins' | awk -F'"' '{
+# if($0 ~ /Plugins for Private sites/)
+# exit
+# if($0 ~ /\.py/)
+# {
+# url=$2
+# getline
+# getline
+# getline
+# if($0 !~ /❗|✖/)
+# print url
+# }
+#}' | xargs curl -s -Z -L --remote-name-all --create-dirs --output-dir "$1" --
+
diff --git a/home/xyz/.local/bin/gita b/home/xyz/.local/bin/gita
new file mode 100755
index 00000000..a3d0dbc9
--- /dev/null
+++ b/home/xyz/.local/bin/gita
@@ -0,0 +1,18 @@
+#!/bin/sh
+# note message string must come after options
+
+cmd='git'
+
+while getopts clLsS f; do
+ case $f in
+ c|l|L|s|S) cmd="cfg -$f";;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+mes="${1:-refactor}"
+
+$cmd add -u
+$cmd commit -m "$mes"
+$cmd push
diff --git a/home/xyz/.local/bin/gitfork b/home/xyz/.local/bin/gitfork
new file mode 100755
index 00000000..cefc4f68
--- /dev/null
+++ b/home/xyz/.local/bin/gitfork
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+git clone "$1"
+cd "$(basename "$1" .git)" || exit
+git remote add upstream "$2"
+git branch fly
+git checkout fly
+git push --set-upstream origin fly
diff --git a/home/xyz/.local/bin/gitpu b/home/xyz/.local/bin/gitpu
new file mode 100755
index 00000000..fcc32be9
--- /dev/null
+++ b/home/xyz/.local/bin/gitpu
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+ git checkout master
+ git pull upstream master
+ git push
+ git checkout fly
+ git merge --no-edit master
+ git push
+else
+ for dir ; do
+ git -C "$dir" checkout master
+ git -C "$dir" pull upstream master
+ git -C "$dir" push
+ git -C "$dir" checkout fly
+ git -C "$dir" merge --no-edit master
+ git -C "$dir" push
+ done
+fi
diff --git a/home/xyz/.local/bin/mmi b/home/xyz/.local/bin/mmi
new file mode 100755
index 00000000..b3b5868f
--- /dev/null
+++ b/home/xyz/.local/bin/mmi
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+mi ()
+{
+ sudo make -B install
+}
+
+origin="$PWD"
+cmd='mi'
+
+while getopts q opt; do
+ case $opt in
+ q) cmd='qmake6;mi';;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+if [ -z "$1" ]; then
+ eval "$cmd"
+else
+ for dir ; do
+ cd "$dir" || exit
+ eval "$cmd"
+ cd "$origin" || exit
+ done
+fi
diff --git a/home/xyz/.local/bin/mpva b/home/xyz/.local/bin/mpva
new file mode 100755
index 00000000..7224cf46
--- /dev/null
+++ b/home/xyz/.local/bin/mpva
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+if [ $# -eq 0 ]; then
+ # fzf has --read0, maybe useful
+ find "$XDG_MUSIC_DIR" | fzf -m --print0 | xargs -0 mpv --af= --shuffle --loop-playlist=inf --video=no
+elif [ $# -eq 1 ]; then
+ # use --loop-file for single file, better performance maybe
+ mpv --af= --loop-file=inf --video=no "$1"
+else
+ mpv --af= --shuffle --loop-playlist=inf --video=no "$@"
+fi
diff --git a/home/xyz/.local/bin/mpvy b/home/xyz/.local/bin/mpvy
new file mode 100755
index 00000000..4471b668
--- /dev/null
+++ b/home/xyz/.local/bin/mpvy
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# references:
+# https://www.rockyourcode.com/til-how-to-watch-youtube-videos-with-mpv-and-keyboard-shortcuts/
+
+fps=30
+height=$SCR_HEIGHT
+url="$(xsel -ob)"
+flag=s
+
+# option f and h may do nothing if redownload? since same filename exist. yt-dlp won't download same file even without --auto-file-renameing=false. how improve? pass argument to aria2c?
+while getopts Aabd:f:h:su: opt; do
+ case $opt in
+ # s: streaming, a: aria2c then mpv, A: aria2c
+ A|a|s) flag=$opt;;
+ b) format='bestvideo+bestaudio/best';;
+ d) download_dir="$OPTARG";;
+ f) fps="$OPTARG";;
+ h) height="$OPTARG";;
+ u) url="$OPTARG";;
+ \?) exit 1;;
+ esac
+done
+if [ -z "$format" ]; then
+ vformat="[height<=?$height][fps<=?$fps][vcodec!^=?vp9][vcodec!^=?av01]"
+ # usually get .mp4 video + .webm audio on youtube, yt-dlp needs to merge them to .mkv
+ # it used to cause problem, but I forget
+ format="bestvideo$vformat+bestaudio/best$vformat"
+fi
+
+case $flag in
+ # here if use --write-sub, mpv doesn't recognize subtitles?
+ # --embed-subs is a little bit better, but still worse then direct streaming
+ A) yt-dlp -f "$format" --embed-subs -P "${download_dir:-"$XDG_DOWNLOAD_DIR/mpvy/"}" --sponsorblock-remove all "$url";;
+ a) yt-dlp -f "$format" --embed-subs -P "${download_dir:-/tmp/mpvy/}" --sponsorblock-remove all "$url" --exec 'mpv --fs --speed=2';;
+ s) mpv --ytdl-format="$format" --ytdl-raw-options='write-sub=' --fs --speed=2 "$url";;
+esac
+# not sure if this is the best practice, but it seems working ;)
+status=$?
+[ $status -ne 0 ] && notify-send 'mpvsel failed'
+exit $status
diff --git a/home/xyz/.local/bin/mvtr b/home/xyz/.local/bin/mvtr
new file mode 100755
index 00000000..850f8dba
--- /dev/null
+++ b/home/xyz/.local/bin/mvtr
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# can use ls pipe to sed pipe to bash, similar to eval? see comment at link:
+# link also show perl-rename approach
+# https://linuxconfig.org/rename-all-files-from-uppercase-to-lowercase-characters
+# if only change ' ' to '_' `rename` might be sufficient, for upper to lower case might need perl-rename if want concise
+
+# ./_filename will be renamed ./filename, not what I want, need improve
+# -exec can't replace -execdir here, can write a -exec version
+
+find "$@" -depth -execdir sh -c 'dest="$(echo "$1" | tr -d "\047" | sed -E -e "s/([[:lower:]])([[:upper:]])/\1_\2/g" | tr "[:upper:] " "[:lower:]_" | tr -s "[:punct:]" | sed -E -e "s#/-#/#g" -e "s/_([[:punct:]])/\1/g" -e "s/([[:punct:]])_/\1/g" | tr -s "[:punct:]")"; [ -e "$dest" ] || mv -v -- "$1" "$dest"' shell '{}' \;
+
+# for test
+#mkdir -p ~/test/A\ A/; rm -rf ~/test/A\ A/1t/; cp -r ~/test/0t/ ~/test/A\ A/1t/; time mvtr ~/test/A\ A/1t/; tree ~/test/A\ A/1t/
diff --git a/home/xyz/.local/bin/o b/home/xyz/.local/bin/o
new file mode 100755
index 00000000..21464974
--- /dev/null
+++ b/home/xyz/.local/bin/o
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# useful links
+# https://stackoverflow.com/questions/3430330/best-way-to-make-a-shell-script-daemon
+# https://serverfault.com/questions/117152/do-background-processes-get-a-sighup-when-logging-off
+# https://wiki.archlinux.org/title/default_applications
+
+# I don't care about errors, I don't want to handle errors, just shut up please.
+# nohup seems not necessary if don't care about daemon SIGHUP requirement
+
+if [ $# -eq 1 ] && [ -e "$1" ] && ! [ -x "$1" ]; then
+ nohup xdg-open "$1" 0<&- >&- 2>&- &
+else
+ nohup "$@" 0<&- >&- 2>&- &
+fi
diff --git a/home/xyz/.local/bin/orgext b/home/xyz/.local/bin/orgext
new file mode 100755
index 00000000..f1654d2c
--- /dev/null
+++ b/home/xyz/.local/bin/orgext
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# references:
+# https://stackoverflow.com/questions/1842254/how-can-i-find-all-of-the-distinct-file-extensions-in-a-folder-hierarchy
+
+# not sure if find here need -depth, not sure if mv here need -n and -T
+
+outdir ()
+{
+ find . -type f -execdir mv -nt "$PWD" -- '{}' \+
+ find . -mindepth 1 -maxdepth 1 -type d -delete
+}
+
+indir ()
+{
+ find . -maxdepth 1 -type f | awk -F. '!a[$NF]++{print $NF}' | xargs mkdir --
+ find . -mindepth 1 -maxdepth 1 -type d -execdir basename -az -- '{}' \+ | xargs -0 -I _ find . -maxdepth 1 -type f -name '*._' -execdir mv -t '_' -- '{}' \+
+}
+
+case "$1" in
+ '-o') outdir;;
+ '-i') indir;;
+ *) outdir; indir;;
+esac
diff --git a/home/xyz/.local/bin/reco b/home/xyz/.local/bin/reco
new file mode 100755
index 00000000..d56e61c2
--- /dev/null
+++ b/home/xyz/.local/bin/reco
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# useful urls:
+# https://github.com/LukeSmithxyz/voidrice/blob/master/.local/bin/dmenurecord
+# https://wiki.archlinux.org/title/FFmpeg#Screen_capture
+# https://trac.ffmpeg.org/wiki/Capture/Desktop
+
+infofile="/tmp/recoinfo"
+recodir="$XDG_VIDEOS_DIR/recordings/"
+mkdir -p "$recodir"
+
+if [ -e "$infofile" ]; then
+ read -r pid tmpfile < "$infofile"
+ kill "$pid"
+ notify-send 'finish recording, start converting'
+ ffmpeg -i "$tmpfile" "$recodir/$(time.uuid).mkv"
+ notify-send 'finish converting'
+ rm "$tmpfile" "$infofile"
+else
+ tmpfile="$(mktemp --suffix '.mkv')"
+ notify-send 'start recording'
+ # arch wiki way, no audio, less cpu use during capturing (fast?), large file size, need convert afterward
+ xrectsel '%w %h %x %y' | xargs sh -c 'ffmpeg -y -loglevel quiet -f x11grab -framerate 25 -s "$2"x"$3" -i "$DISPLAY+$4,$5" -c:v ffvhuff "$1" & echo $!' shell "$tmpfile" | xargs -I {} printf '%s\t%s' '{}' "$tmpfile" > "$infofile"
+ # another way to get subshell ffmpeg child pid
+ #xrectsel '%w %h %x %y' | xargs sh -c 'ffmpeg -y -f x11grab -framerate 25 -s "$2"x"$3" -i "$DISPLAY+$4,$5" -c:v ffvhuff "$1"' shell "$tmpfile" &
+ #printf '%s\t%s' "$(ps -o pid= --ppid $!)" "$tmpfile" > "$infofile"
+fi
diff --git a/home/xyz/.local/bin/sbar b/home/xyz/.local/bin/sbar
new file mode 100755
index 00000000..bd471f4a
--- /dev/null
+++ b/home/xyz/.local/bin/sbar
@@ -0,0 +1,90 @@
+#!/bin/sh
+# steal from pystardust, GPL3 license: https://github.com/pystardust/sbar
+# modified to fit my own need
+
+# INIT
+printf '%s' "$$" > "$XDG_CACHE_HOME/pidofbar"
+sec=0
+
+bool () {
+ read str
+ if [ "$str" = "on" ] || [ "$str" = "up" ] || [ "$str" = "Connected" ]; then
+ echo 1
+ else
+ echo 0
+ fi
+}
+
+# MODULES
+update_time () {
+ time="$(date '+%a %m/%d %H:%M') $(TZ=Asia/Shanghai date '+/%d %H:')"
+}
+
+update_cap () {
+ cap="$(if xset q | grep -q "Caps Lock: *on"; then echo A; else echo a; fi)"
+}
+
+update_net () {
+ net="$(bool < /sys/class/net/wlp6s0/operstate)"
+}
+
+update_vpn () {
+ vpn="$(mullvad status | awk '{print $3}' | bool)"
+}
+
+update_vol () {
+ vol="$(amixer get Master | awk -F'[][]' 'END{printf"%d %s",($4=="on")?1:0,$2}')"
+}
+
+update_mic () {
+ mic="$(amixer get Capture | awk -F'[][]' 'END{print $4}' | bool)"
+}
+
+update_bat () {
+ bat="$(cat /sys/class/power_supply/BAT1/capacity)%"
+}
+
+# For calcurse users, refer https://github.com/pystardust/automeet
+#update_event () {
+# event="$(calcurse -n | sed 1d | \
+# sed -E "s_^ *\[(.*):(.*)\] ([^\t]*)\t?.*_[\1h \2m->\3]_")"
+# [ "[]" = "$event" ] && event=""
+#}
+
+# modules that don't update on their own need to be run at the start for getting their initial value
+update_vol
+update_mic
+update_cap
+
+display () {
+ xsetroot -name "$(echo "$time | N $net V $vpn | M $vol C $mic | $cap | $bat")"
+}
+
+# SIGNALLING
+# trap "<function>;display" "RTMIN+n"
+trap "update_mic;display" "RTMIN"
+trap "update_vol;display" "RTMIN+1"
+# xev can't read my toggle internet keyboard key, don't know what key to use in sxhkd to send signal
+#trap "update_net;display" "RTMIN+2"
+trap "update_cap;display" "RTMIN+3"
+trap "update_vpn;display" "RTMIN+4"
+# to update it from external commands
+## kill -m "$(cat "$XDG_CACHE_HOME/pidofbar")"
+# where m = 34 + n
+
+while true; do
+ # next line of code seems make hotkeys that send kill signals more responsive? not fully understood
+ sleep 1 & wait && {
+ # to update item ever n seconds with a offset of m
+ ## [ $((sec % n)) -eq m ] && udpate_item
+ [ $((sec % 5 )) -eq 0 ] && update_time # update time every 5 seconds
+ [ $((sec % 5 )) -eq 0 ] && update_net
+ [ $((sec % 5 )) -eq 0 ] && update_vpn
+ [ $((sec % 60)) -eq 0 ] && update_bat
+ #[ $((sec % 300)) -eq 1 ] && update_event
+
+ # how often the display updates ( 5 seconds )
+ [ $((sec % 5 )) -eq 0 ] && display
+ sec=$((sec + 1))
+ }
+done
diff --git a/home/xyz/.local/bin/shufwall b/home/xyz/.local/bin/shufwall
new file mode 100755
index 00000000..5b249e12
--- /dev/null
+++ b/home/xyz/.local/bin/shufwall
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+ file=$(find -L "$XDG_PICTURES_DIR/wallpapers" -type f | shuf -n 1)
+else
+ file=$(find -L "$@" -maxdepth 1 -type f | shuf -n 1)
+fi
+
+display -resize "${SCR_WIDTH}x$SCR_HEIGHT" -backdrop -window root "$file"
diff --git a/home/xyz/.local/bin/time.uuid b/home/xyz/.local/bin/time.uuid
new file mode 100755
index 00000000..11cc67ad
--- /dev/null
+++ b/home/xyz/.local/bin/time.uuid
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# echo current nanosecond since epoch and alpha-numerically ordered UUID
+# https://stackoverflow.com/questions/28681650/generate-alpha-numerically-ordered-uuids-over-time?noredirect=1&lq=1
+# https://askubuntu.com/questions/342842/what-does-this-command-mean-awk-f-print-4
+echo "$(date '+%s.%N').$(uuidgen -t | awk -F- '{OFS="-"; print $3,$2,$1,$4,$5}')"
diff --git a/home/xyz/.local/bin/topa b/home/xyz/.local/bin/topa
new file mode 100755
index 00000000..e921bc31
--- /dev/null
+++ b/home/xyz/.local/bin/topa
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+o alacritty -e htop
+o alacritty -e radeontop -c
+o alacritty -e sudo iotop
+o alacritty -e sudo nethogs
diff --git a/home/xyz/.local/bin/upd b/home/xyz/.local/bin/upd
new file mode 100755
index 00000000..65452b37
--- /dev/null
+++ b/home/xyz/.local/bin/upd
@@ -0,0 +1,120 @@
+#!/bin/sh
+
+all ()
+{
+ fast
+ usb
+ qb
+ ncm
+ kg
+ refl
+ gall
+}
+
+# not in all
+clean ()
+{
+ nsxiv -c
+ # my ways
+ # -exec can't replace -execdir here
+ find "$XDG_CACHE_HOME/nsxiv/" -depth -type d -empty -execdir rmdir -- '{}' \+
+ # -exec can replace -execdir here
+ #find "$XDG_CACHE_HOME/nsxiv/" -depth -type d -execdir rmdir --ignore-fail-on-non-empty -- '{}' \+
+ # nsxiv man page way
+ #find "$XDG_CACHE_HOME/nsxiv/" -depth -type d -empty ! -name '.' -exec rmdir -- '{}' \;
+ rm "$HOME/.mozilla/firefox/xxxxxxxx.fly/prefs.js.backup."*
+}
+
+fast ()
+{
+ pac
+ misc
+}
+
+gall ()
+{
+ gallery-dl --download-archive "$XDG_DOCUMENTS_DIR/database/gallery-dl.sqlite3" -d "$XDG_PICTURES_DIR/anime/gallery-dl/" -i "$XDG_CONFIG_HOME/myconf/gallery_urls"
+}
+
+userjs ()
+{
+ kill $(pidof "$BROWSER")
+ "$HOME/.mozilla/firefox/xxxxxxxx.fly/prefsCleaner.sh" -s
+ "$HOME/.mozilla/firefox/xxxxxxxx.fly/updater.sh" -us
+}
+
+g ()
+{
+ curlkg -u649b9e82272a348b -- "$XDG_MUSIC_DIR/not_pure/lan_lan/kg/"
+ curlkg -u64949d822c25328c -- "$XDG_MUSIC_DIR/not_pure/cheng_ruan/kg/"
+}
+
+misc ()
+{
+ cfg -c submodule foreach git pull
+ nvim +PlugUpgrade +PlugClean! +PlugUpdate +qa
+ tldr --update
+ sudo hardcode-fixer
+ userjs
+}
+
+ncm ()
+{
+ curlncm -a48860966 -- "$XDG_MUSIC_DIR/not_pure/lan_lan/ncm/artist/"
+ curlncm -a46703185 -- "$XDG_MUSIC_DIR/not_pure/cheng_ruan/ncm/artist/"
+ curlncm -r793052426 -- "$XDG_MUSIC_DIR/not_pure/lan_lan/ncm/djradio/"
+ curlncm -r792968433 -- "$XDG_MUSIC_DIR/not_pure/cheng_ruan/ncm/djradio/"
+}
+
+pac ()
+{
+ sudo pacman --noconfirm -Syu
+ paru --noconfirm -aSyu
+ # part steal from aur comment
+ # sometimes "ERROR: Failure while downloading": https://github.com/neovim/neovim/issues/15709
+ rm -rf ~/.cache/paru/clone/neovim-nightly-bin/ && yes 1 | PARU_PAGER=cat paru --rebuild --redownload neovim-nightly-bin
+ sudo pacman -Fy
+}
+
+qb ()
+{
+ rm -r -- "$HOME/programs/qbittorrent_search_plugins/"
+ curlqb "$HOME/programs/qbittorrent_search_plugins/"
+}
+
+refl ()
+{
+ sudo reflector --save /etc/pacman.d/mirrorlist --latest 200 --sort rate
+}
+
+usb ()
+{
+ cfg -c push usb
+ cfg -l push usb
+ cfg -s push
+ git --git-dir="$XDG_DOCUMENTS_DIR/notes/.git" --work-tree="$XDG_DOCUMENTS_DIR/notes/" push usb
+}
+
+
+if [ $# -eq 0 ]; then
+ fast
+else
+ while getopts acfgjkmnpqru opt; do
+ case $opt in
+ a)all;;
+ # not in all
+ c)clean;;
+ f)fast;;
+ g)gall;;
+ j)userjs;;
+ k)kg;;
+ m)misc;;
+ n)ncm;;
+ p)pac;;
+ q)qb;;
+ r)refl;;
+ u)usb;;
+ \?)exit 1;;
+ esac
+ done
+fi