X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=brc2;h=7e57ec94117f4b70dc76e50a6a3ebd2736a4cfa6;hb=9c77c557e60d21caceeef1e78e35b35ed968fca9;hp=6ee207c10b126f440bd4952fff223a3a8fda2f7f;hpb=ad6ea16ea6f2a8676527257fb6afb659f2cee475;p=distro-setup diff --git a/brc2 b/brc2 index 6ee207c..7e57ec9 100644 --- a/brc2 +++ b/brc2 @@ -364,7 +364,7 @@ ap() { } aw() { pushd /a/work/ans >/dev/null - time ansible-playbook -v -i inventory adhoc.yml "$@" + time ansible-playbook -i inventory adhoc.yml "$@" popd >/dev/null } ad() { @@ -380,6 +380,264 @@ astudio() { /a/opt/android-studio/bin/studio.sh "$@" &r; } + +iki() { + local url path + if [[ $1 ]]; then + path="$*" + else + read -r -p "enter path" path + fi + url=$(readlink -f "$path") + url="https://brains.fsf.org/wiki/${url#*brains/}" + url="${url%.mdwn}" + echo "$url" + # /f/brains/sysadmin/interns/2022/nick_shrader/intro_blog_post.mdwn + # becomes + # https://brains.fsf.org/wiki/sysadmin/interns/2022/nick_shrader/intro_blog_post + +} + +# Generate beet smartplaylists for navidrome. +# for going in the reverse direction, run +# /b/ds/navidrome-playlist-export +beetsmartplaylists() { + install -m 0700 -d /tmp/ianbeetstmp + beet splupdate + # kill off any playlists we deleted. they will still need manual + # killing from a navidrome client. + rm -rf /i/converted/beetsmartplaylists + mkdir -p /i/converted/beetsmartplaylists + for f in /tmp/ianbeetstmp/*; do + sed 's,^/i/m,/i/converted,;s,\.flac$,.mp3,' "$f" >"/i/converted/beetsmartplaylists/${f##*/}" + rm "$f" + done + rmdir /tmp/ianbeetstmp +} + +# Export beets ratings into navidrome +beetrating() { + local tmp tmpfile myuser userid rating path cpath sqlpath + # plucked this from the db. im the only user. + userid=23cc2eb9-e35e-4811-a0f0-d5f0dd6eb634 + tmpfile=$(mktemp) + beet ls -f '$rating $path' ^genre:spoken-w ^genre:skit rating:2..5 >$tmpfile + while read -r rating path; do + tmp="/i/converted${path#/i/m}" + cpath="${tmp%.*}.mp3" # converted path + sqlpath="${cpath//\'/\'\'}" + old_rating=$(sqlite3 /i/navidrome/navidrome.db "select rating from annotation inner join media_file on item_id = id where path = '$sqlpath' and item_type = 'media_file';") + if [[ $old_rating ]]; then + if [[ $old_rating != $rating ]]; then + # https://stackoverflow.com/a/50317320 + m sqlite3 /i/navidrome/navidrome.db " +update annotation set rating = $rating + where item_id in ( + select media_file.id from annotation inner join media_file on annotation.item_id = media_file.id + where media_file.path = '$sqlpath' and annotation.item_type = 'media_file' );" + fi + else + # /a/opt/navidrome/persistence/sql_annotations.go v0.48.0 + # https://www.sqlite.org/lang_insert.html + m sqlite3 /i/navidrome/navidrome.db "insert into annotation select '$(uuidgen)', '$userid', id, 'media_file', 0, NULL, $rating, 0, NULL from media_file where path = '$sqlpath';" + fi + #sqlite3 /i/navidrome/navidrome.db "select path from annotation inner join media_file on item_id = id where rating = $r;" + done <$tmpfile +} + +# Do transcoding and hardlinking of audio files for navidrome. +# +# Deletes files in the converted directory which should no longer +# be there due to a rename of the unconverted file. +beetconvert() { + # directs to avoid printing every file + beet convert -y ^genre:spoken-w ^genre:skit ^rating:1 >/dev/null 2> >(grep -v '^convert: Skipping' ||:) + local l + local -A paths + while read -r l; do + convertedpath="/i/converted${l#/i/m}" + case $convertedpath in + *.flac) convertedpath="${convertedpath%.flac}.mp3" ;; + esac + paths[$convertedpath]=t + done < <(beet ls -f '$path' ^genre:spoken-w ^genre:skit ^rating:1) + while read -r l; do + if [[ ! ${paths[$l]} ]]; then + rm -v "$l" + fi + done < <(find /i/converted -path /i/converted/beetsmartplaylists -prune -o \( -type f -print \)) +} + +# tag with beets. +# usage: beetag QUERY +# it lists the query, reads an input char for tagging one by one +# 1-5 = set rating +# a-z+ = set genre/playlist. +# enter = next song +# , = play song +beetag() { + if (( ! $# )); then + echo beetag: error expected a query arg >&2 + return 1 + fi + local last_genre_i fstring tag id char new_item char_i genre tag remove + local -a genres pl_tags buttons button_map ids tags + local -A button_i + genres=( + ambient + avant + blues + classical + country + # like power glove + dark-wave + hardcore + instrumental + jazz + latin + metal + musical + # mq = mac quale. similar to the mr robot soundtracks. + # slow, foreboding. usually electronic. + mq + noise + pop + rap + rock + skit + spoken-w + techno + world + ) + pl_tags=( + expl + love + pump1 + pumprap + rend + run + sad + ) + last_genre_i=$(( ${#genres[@]} - 1 )) + buttons=( {a..z} 0 {6..9} ) + button_map=(${genres[@]} ${pl_tags[@]}) + fstring= + for tag in "${pl_tags[@]}"; do + fstring+="%ifdef{$tag,$tag }" + done + + for (( i=0; i<${#buttons[@]}; i++ )); do + button_i[${buttons[i]}]=$i + done + beet ls -f '%ifdef{rating,$rating }'"$fstring"', $genre $artist - $album - $title' "$@" + hr + mapfile -t ids < <(beet ls -f '$id' "$@") + for id in "${ids[@]}"; do + lsout="$(beet ls -f '%ifdef{rating,$rating }'"$fstring"', $genre $id $artist - $album - $title' "id:$id")" + tags=( ${lsout%%,*} ) + printf "%s\n" "$lsout" + for (( i=0; i<${#button_map[@]}; i++ )); do + echo ${buttons[i]} ${button_map[i]} + done + while true; do + read -r -N 1 -s char + if [[ $char == $'\n' ]]; then + break + fi + case $char in + ,) + beet play "id:$id" + continue + ;; + [1-5]) + beet modify -y "id:$id" rating=$char + continue + ;; + esac + char_i=${button_i[$char]} + new_item=${button_map[$char_i]} + if [[ ! $char_i || ! $new_item ]]; then + echo "error: no mapping of input found, try again" + continue + fi + if (( char_i <= last_genre_i )); then + m beet modify -y "id:$id" genre=$new_item + else + remove=false + for tag in ${tags[@]}; do + if [[ $new_item == "$tag" ]]; then + remove=true + break + fi + done + if $remove; then + m beet modify -y "id:$id" "$new_item!" + else + m beet modify -y "id:$id" $new_item=t + fi + fi + done + done + + # sadpop + # + # rending: + # two dollar guitar: speed + # black heard procession + # strong enough sheryl crow + # + # +} + +# escape regex. +# +# This is not perfect but generally good enough. It escapes all +# metachars listed man 3 pcrepattern. +er() { + sed 's/[]\\^$.[|()?*+{}]/[&]/g; s/\^/\\^/g' <<<"$*" +} + +# usage beegenre QUERY +# +# beet set genre for QUERY based on existing artist most used genre on +# +# inverse of query for each artist found in QUERY. If query starts with +# "artist:" it is used as the artist instead of each artist in QUERY. +# +beegenre() { + local artist artregex genre term singleartist + local -a artists genres terms + singleartist=false + case $1 in + artist:*) + singleartist=true + artist="$term" + ;; + esac + if $singleartist; then + read count genre < <(beet ls -f '$genre' "$artist" "${@/#/^}" | sort | uniq -c | sort -n | tail -n1) ||: + beet modify "$artist" "$@" genre=$genre + else + while read -r artist; do + artregex=$(er "$artist") + read count genre < <(beet ls -f '$genre' "artist::^$artregex$" "${@/#/^}" | sort | uniq -c | sort -n | tail -n1) || continue + if [[ $count ]]; then + artists+=("$artregex") + genres+=("$genre") + echo "beet modify -y $@ \"artist::^$artist$\" genre=$genre # $count" + fi + done < <(beet ls -f '$artist' "$@" | sort -u) + read -r -N 1 -s -p "Y/n " char + case $char in + [Yy$'\n']) + for (( i=0; i<${#artists[@]}; i++ )); do + beet modify -y "$@" "artist::^${artists[i]}$" genre=${genre[i]} + done + ;; + esac + fi +} + # note, to check for glue records # First, find some the .org nameservers: # dig +trace iankelling.org @@ -414,7 +672,7 @@ bbk() { # btrbk wrapper install-my-scripts # todo: consider changing this to srun and having the args come # from a file like /etc/default/btrbk, like is done in exim - s jrun btrbk-run "$@" + s jdo btrbk-run "$@" if $active; then if (( ret )); then echo bbk: WARNING: btrbk.timer not restarted due to failure @@ -454,40 +712,90 @@ locat() { # log-once cat ngreset } -# duplicated somewhat below. -jrun() { # journal run. run args, log to journal, tail and grep the journal. - # Note, an alternative without systemd would be something like ts. - # Note, I tried using systemd-cat, but this seems obviously better, - # and that seemed to have a problem exiting during a systemctl daemon-reload - local cmd_name jr_pid s +scr() { + screen -RD "$@" +} + + +# version of jdo for my non-root user +jdo() { + # comparison of alternative logging methods: + # + # systemd-run command (what this function does) + # + # If there is a user prompt, the program will detect that it is not + # connected to a terminal and act in a non-interactive way, skipping + # the prompt. This has the benefit that you know exactly how the + # program will act if you want to move it into a service that runs + # automatically. + # + # If run with sudo and command is a shell script which does a sleep, + # it can (sometimes?) output some extra whitespace in front of + # messages, more for each subsequent message. This can be avoided by + # becoming root first. + # + # It logs the command's pid and exit code, which is nice. + # + # + ### command |& ts | tee file.log + # + # If there is a user prompt, like "read -p prompt var", it will hang + # without outputting the prompt. + # + # I've had a few times where ts had an error and I wasn't totally sure + # if it was really the command or ts having the problem. + # + # Sometimes some output will get hidden until you hit enter. + # + # + ### command |& pee cat logger + # + # This seems to work. I need to test more. + # + # + ### command |& logger -s + # + # User prompts get confusingly prefixed to earlier output, and all log + # entries get prefixed with annoying priority level. + # + # + ### systemd-cat + # + # Had a few problems. One major one is that it exited in the middle of + # a command on systemctl daemon-reload + # + # Related commands which can log a whole session: script, sudo, screen + local cmd cmd_name jr_pid ret ret=0 - cmd_name=${1##*/} - cmd=$1 + cmd="$1" + shift + cmd_name=${cmd##*/} if [[ $cmd != /* ]]; then - cmd=$(which $1) + cmd=$(type -P "$cmd") fi + # -q = quiet journalctl -qn2 -f -u "$cmd_name" & - # Guess of time needed to avoid missing initial lines. + # Trial and error of time needed to avoid missing initial lines. # .5 was not reliable. 1 was not reliable. 2 was not reliable sleep 4 - # We kill this in prompt-command for the case that we ctrl-c the - # systemd-cat. i dont know any way to trap ctrl-c and still run the - # normal action for it. There might be a way, unsure. jr_pid=$! # note, we could have a version that does system --user, but if for example # it does sudo ssh, that will leave a process around that we can't kill # and it will leave the unit hanging around in a failed state needing manual # killing of the process. - m s systemd-run --uid $(id -u) --gid $(id -g) \ + s systemd-run --uid $(id -u) --gid $(id -g) \ -E SSH_AUTH_SOCK=/run/openssh_agent \ - --unit "$cmd_name" --wait --collect "$cmd" "${@:2}" || ret=$? - # This justs lets the journal output its last line + --unit "$cmd_name" --wait --collect "$cmd" "$@" || ret=$? + # The sleep lets the journal output its last line # before the prompt comes up. sleep .5 kill $jr_pid &>/dev/null ||: unset jr_pid fg &>/dev/null ||: + # this avoids any err-catch + (( $ret == 0 )) || return $ret } + # service run, and watch the output srun() { local unit @@ -511,7 +819,7 @@ sm() { # switch mail host s ssh-add /root/.ssh/home fi install-my-scripts - s jrun switch-mail-host "$@" + s jdo switch-mail-host "$@" return $ret } sh2() { # switch host2 @@ -524,7 +832,7 @@ sh2() { # switch host2 s ssh-add /root/.ssh/home fi install-my-scripts - s jrun switch-host2 "$@" + s jdo switch-host2 "$@" return $ret } @@ -1218,7 +1526,7 @@ lom() { m sudo losetup $l $fs_file fi if ! sudo cryptsetup status /dev/mapper/$base &>/dev/null; then - if ! sudo cryptsetup luksOpen $l $base; then + if ! m sudo cryptsetup luksOpen $l $base; then m sudo losetup -d $l return 1 fi @@ -1369,6 +1677,9 @@ mpvm() { # get page source of https://en.wikipedia.org/w/index.php?title=Video_file_format&action=edit # into /a/x.log, then # grep '^| *\.' /a/x.log | sed 's/| *//;s/,//g' + + # note: to join them together for a regex, do: + # old=; for e in ${extensions[@]/./}; do if [[ ! $old ]]; then old=$e; continue; fi; echo -n "$old|"; old=$e; done; echo $e extensions=( .webm .mkv @@ -1435,6 +1746,35 @@ allmyirc() { ssh root@iankelling.org "cd $d; find . -mtime -60 -type f -exec grep '\