X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;ds=sidebyside;f=brc2;h=7e57ec94117f4b70dc76e50a6a3ebd2736a4cfa6;hb=9c77c557e60d21caceeef1e78e35b35ed968fca9;hp=61bd93109a60b49b3e78c64b06f0bdbf680ae124;hpb=d6def754cd241538c61456536b52ee51cbd85b42;p=distro-setup diff --git a/brc2 b/brc2 index 61bd931..7e57ec9 100644 --- a/brc2 +++ b/brc2 @@ -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 @@ -1268,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 @@ -1419,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