From e6ce0e7dea092cce12599cba59895dcd54b0ea1c Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Sat, 1 Mar 2025 14:52:40 -0500 Subject: [PATCH] more beetag refactoring --- beet-data | 10 +++ beetag | 228 ++++++++++++++++++++++++++++++++++--------------- brc | 20 +---- brc2 | 83 +----------------- fsf-script-lib | 20 +++++ 5 files changed, 193 insertions(+), 168 deletions(-) diff --git a/beet-data b/beet-data index cccc655..8af190b 100644 --- a/beet-data +++ b/beet-data @@ -121,6 +121,16 @@ tags=( sad ) +declare -A bpla # beet playlist associative array +beetapl() { # beet add playlist + local name + name="$1" + shift + # shellcheck disable=SC2034 # unused from beetag, don't care for now. + bpla[$name]="${*@Q}" +} + + # this function is just so we can have some local vars # and not mess with the global var namespace. beet-gen-global-vars() { diff --git a/beetag b/beetag index 84d7f52..e786bc1 100755 --- a/beetag +++ b/beetag @@ -20,9 +20,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Uncomment this when developing this file for quick shellcheck results, +# otherwise it is redundant. . /a/c/fsf-script-lib - # Must be called from beetag for variables to be setup beetag-help() { source /a/bin/ds/beet-data @@ -65,6 +66,80 @@ EOF fi } +# beet modify quietly +beetmq() { + local tmpf + tmpf="$(mktemp)" + # a bunch of effort to ignore output we dont care about... + sed 's/^format_item:.*/format_item: ignore_this/' ~/.config/beets/config.yaml >$tmpf + beet -c $tmpf modify -y "$@" > >(grep -vFx -e 'ignore_this' -e 'Modifying 1 items.' ||:) + rm "$tmpf" + beetag-nostatus 1 +} + +kill-bg-quiet() { + # https://stackoverflow.com/a/5722874 + kill %% 2>/dev/null ||:; wait %% 2>/dev/null ||: +} + + +# run if not running. +# +# Note: this does not work with shell scripts as they are normally +# invoked, because the ps output has the interpreter at the start. +# A workaround is to invoke the command in that format, or we could +# do various other workarounds. +# +# background, this relies on how ps converts newlines in arguments to spaces, and +# assumes we won't be searching for a command with spaces in its arguments +rinr() { + # shellcheck disable=SC2009 # pgrep has no fixed string option, plus see above. + if ps h -o args -C "${1##*/}" | grep -Fxqv "$*" &>/dev/null || [[ $? == 141 ]]; then + "$@" + fi +} +# variation of above: run or wait if running +rowir() { + local pid + pid=$(ps h -o 'pid,args' -C "${1##*/}" | sed -r 's/^[[:space:]]*([0-9]+)[[:space:]](.*)/\1\n\2/' | grep -B1 -Fx "$*" | head -n1 ||: ) + if [[ $pid ]]; then + # https://unix.stackexchange.com/questions/427115/listen-for-exit-of-process-given-pid + tail --pid="$pid" -f /dev/null + else + "$@" + fi +} + + +mpvrpc-loadfile() { + local path nextpath cachedir finalpath nextpath count + cachedir=$HOME/.iank-music-cache + path="$1" + nextpath="$2" + + # note: logic duplicated in beetpull + local remote_p=true + source /p/c/domain-info + if [[ $HOSTNAME == "$d_host" ]]; then + remote_p=false + fi + + if $remote_p; then + finalpath="$cachedir${path#/i/m}" + rowir rsync --partial -a --inplace --mkpath "b8.nz:$path" "$finalpath" + finalnextpath="$cachedir${nextpath#/i/m}" + count=$(pgrep -a -f "^rsync --partial -a --inplace --mkpath $cachedir" || [[ $? == 1 ]] ) + # allow us to start 2 rsyncs in the background + if [[ $count == [01] ]]; then + rinr rsync --partial -a --inplace --mkpath "b8.nz:$nextpath" "$finalnextpath" & + fi + else + finalpath="$path" + fi + mpvrpc '{ "command": ["loadfile", "'"$finalpath"'"] }' +} + + # Must be called from beetag for variables to be setup beetag-nostatus() { if (( $# )); then @@ -108,8 +183,10 @@ mpvrpc-percent-pos() { # # Sets variables: pl_state_path pl_seed_path. Does mkdir of their directory. # +# Creates/recreates $pl_seed_path if needed. +# # Input vars: random -beetag-pl-state-init() { +pl-state-init() { local seed_num seed_file pl_state_dir pl_state_file # note: this structure of files is rather haphazard. seed_num=1 # later we might want a few @@ -131,8 +208,74 @@ beetag-pl-state-init() { pl_state_path=$pl_state_dir/$pl_state_file pl_seed_path=$pl_state_dir/$seed_file + if $new_random || [[ ! -r $pl_seed_path ]]; then + { base64 < /dev/urandom | head -c 200 ||:; echo; } > $pl_seed_path + fi + } +# Call from beetag. +# +# Sets do_rare_genres, button_map, last_genre_i, button_i. Toggles those +# between rare and common genres. +# +# Input vars: buttons, common_genres, pl_tags +toggle-rare-genres() { + + # starts as off if not yet set. + if $do_rare_genres || [[ ! $do_rare_genres ]]; then + do_rare_genres=false + button_map=(${common_genres[@]} ${pl_tags[@]}) + last_genre_i=$(( ${#rare_genres[@]} - 1 )) + else + do_rare_genres=true + button_map=(${rare_genres[@]} ${pl_tags[@]}) + last_genre_i=$(( ${#rare_genres[@]} - 1 )) + fi + for (( i=0; i<${#buttons[@]}; i++ )); do + button_i[${buttons[i]}]=$i + done + + } + +# Call from beetag. Queries our songlist and parses it into variables. +# +# Sets ls_lines, id_count, paths, ids +# +# Input vars: pl_tags, pl_seed_path, random, beet_query +beetag-ls-setup() { + local fmt tag_query tmpstr line tag ls_line line_no_path right_pad + local -a ls_out + + # note: PijokVipiotOzeph is just a random string for a delimiter + # shellcheck disable=SC2016 # false positive + fmt='%ifdef{rating,$rating }'"$tag_query"'$genre | $title - $artist - $album $length $id PijokVipiotOzeph $path' + + + for tag in "${pl_tags[@]}"; do + tag_query+="%ifdef{$tag,$tag }" + done + + # shellcheck disable=SC2016 # obvious reason + tmpstr=$(beet ls -f "$fmt" "${beet_query[@]}" | { if $random; then sort -R --random-source=$pl_seed_path; else cat; fi; } ) + mapfile -t ls_out <<<"$tmpstr" + if [[ ! ${ls_out[0]} ]]; then + echo "beetag: error: no result from beet ls ${beet_query[*]}" + return 1 + fi + id_count=${#ls_out[@]} + + for line in "${ls_out[@]}"; do + paths+=("${line#*PijokVipiotOzeph }") + line_no_path="${line% PijokVipiotOzeph*}" + ids+=("${line_no_path##* }") + right_pad="${line_no_path%% |*}" + ls_line="$(printf %-11s "$right_pad")${line_no_path#"$right_pad"}" + ls_lines+=("$ls_line") + done + + } + # tag with beets. # usage: beetag [-r] [-s] QUERY # it lists the query, reads an input char for tagging one by one. @@ -155,12 +298,11 @@ beetag-pl-state-init() { # todo: enter should also unpause beetag() { source /a/bin/ds/beet-data - local last_genre_i tag_query tag id char new_item char_i tag remove doplay i j random path - local do_rare_genres read_wait line lsout ls_line skip_lookback - local escape_char escaped_input expected_input skip_input_regex right_pad erasable_line seek_sec - local pl_state_path tmpstr - local new_random pl_seed_path fmt first_play repeat1 - local -a buttons button_map ids tags tmp_tags initial_ls ls_lines paths + local last_genre_i tag id char new_item char_i tag remove doplay i j random path + local read_wait line lsout ls_line skip_lookback do_rare_genres pl_state_path + local escape_char escaped_input expected_input skip_input_regex erasable_line seek_sec + local new_random pl_seed_path first_play repeat1 + local -a buttons button_map ids tags tmp_tags ls_lines paths beet_query local -A button_i local -i i j volume scrolled id_count line_int skip_start pre_j_count head_count skip_lookback local -i overflow_lines overflow @@ -191,60 +333,20 @@ beetag() { echo beetag: error expected a query arg >&2 return 1 fi + beet_query=("$@") ### end arg processing ### readonly -a buttons=( {a..p} {r..w} {6..8} , . / - "=") - # note: I used to do beetpull here, but mpv + ssfs on slowish - # connection leads to bad/buggy result. - - do_rare_genres=false + ### begin control knob vars ### volume=70 - read_wait=2 doplay=true + ### end control knob vars ### - last_genre_i=$(( ${#common_genres[@]} - 1 )) - - button_map=(${common_genres[@]} ${pl_tags[@]}) - - for (( i=0; i<${#buttons[@]}; i++ )); do - button_i[${buttons[i]}]=$i - done - - beetag-pl-state-init - - if $new_random || [[ ! -r $pl_seed_path ]]; then - { base64 < /dev/urandom | head -c 200 ||:; echo; } > $pl_seed_path - fi - - tag_query= - for tag in "${pl_tags[@]}"; do - tag_query+="%ifdef{$tag,$tag }" - done - # note: PijokVipiotOzeph is just a random string for a delimiter - # shellcheck disable=SC2016 # false positive - fmt='%ifdef{rating,$rating }'"$tag_query"'$genre | $title - $artist - $album $length $id PijokVipiotOzeph $path' - # shellcheck disable=SC2016 # obvious reason - tmpstr=$(beet ls -f "$fmt" "$@" | { if $random; then sort -R --random-source=$pl_seed_path; else cat; fi; } ) - mapfile -t initial_ls <<<"$tmpstr" - if [[ ! ${initial_ls[0]} ]]; then - echo "beetag: error: no result from beet ls $*" - return 1 - fi - id_count=${#initial_ls[@]} - for line in "${initial_ls[@]}"; do - path="${line#*PijokVipiotOzeph }" - # https://github.com/koalaman/shellcheck/issues/2171 - # shellcheck disable=SC2190 # bug in shellcheck, looking at paths from an earlier function - paths+=("$path") - line_no_path="${line% PijokVipiotOzeph*}" - id="${line_no_path##* }" - ids+=("$id") - right_pad="${line_no_path%% |*}" - ls_line="$(printf %-11s "$right_pad")${line_no_path#"$right_pad"}" - ls_lines+=("$ls_line") - i=$(( i+1 )) - done + read_wait=2 + toggle-rare-genres + pl-state-init + beetag-ls-setup j=0 if [[ $playlist ]]; then @@ -396,19 +498,7 @@ beetag() { return ;; y) - if $do_rare_genres; then - do_rare_genres=false - button_map=(${common_genres[@]} ${pl_tags[@]}) - last_genre_i=$(( ${#rare_genres[@]} - 1 )) - else - do_rare_genres=true - button_map=(${rare_genres[@]} ${pl_tags[@]}) - last_genre_i=$(( ${#rare_genres[@]} - 1 )) - fi - local -A button_i - for (( i=0; i<${#buttons[@]}; i++ )); do - button_i[${buttons[i]}]=$i - done + toggle-rare-genres for (( i=0; i<${#button_map[@]}; i++ )); do echo ${buttons[i]} ${button_map[i]} done @@ -571,5 +661,3 @@ beetag() { fi done } - -beetag "$@" diff --git a/brc b/brc index 0685d98..1c42e79 100644 --- a/brc +++ b/brc @@ -1925,25 +1925,6 @@ re() { grr -m 5 "$@" } -# horizontal row. used to break up output -hr() { - local start end end_count arg - # 180 is long enough. 5 for start. - start=█████ end=█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ - end_count=$(( ${COLUMNS:-180} - 5 )) - arg="$*" - if [[ $arg ]]; then - end_count=$(( end_count - 2 - ${#arg} )) - start="$start $arg " - fi - if (( end_count >= 1 )); then - end=${end:0:$end_count} - else - - end= - fi - printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)$start$end$(tput sgr0 2>/dev/null||:)" -} # highlighted echo. hl() { local col input_len=0 @@ -1958,6 +1939,7 @@ hl() { fi echo } + # hl, then run. hlm() { hl "$*"; "$@"; } diff --git a/brc2 b/brc2 index 82fb271..5639e0e 100644 --- a/brc2 +++ b/brc2 @@ -670,15 +670,6 @@ beetconvert-rm-extras() { rm "$tmpf" } -declare -A bpla # beet playlist associative array -beetapl() { # beet add playlist - local name - name="$1" - shift - bpla[$name]="${*@Q}" -} - - beets-gen-playlists() { local i str local -a query_array query_str @@ -719,21 +710,6 @@ bpl() { complete -W "${!bpla[*]}" bpl -# beet modify quietly -beetmq() { - local tmpf - tmpf="$(mktemp)" - # a bunch of effort to ignore output we dont care about... - sed 's/^format_item:.*/format_item: ignore_this/' ~/.config/beets/config.yaml >$tmpf - beet -c $tmpf modify -y "$@" > >(grep -vFx -e 'ignore_this' -e 'Modifying 1 items.' ||:) - rm "$tmpf" - beetag-nostatus 1 -} - -kill-bg-quiet() { - # https://stackoverflow.com/a/5722874 - kill %% 2>/dev/null ||:; wait %% 2>/dev/null ||: -} # debug variables dv() { @@ -743,61 +719,10 @@ dv() { echo } - -# run if not running. -# -# Note: this does not work with shell scripts as they are normally -# invoked, because the ps output has the interpreter at the start. -# A workaround is to invoke the command in that format, or we could -# do various other workarounds. -# -# background, this relies on how ps converts newlines in arguments to spaces, and -# assumes we won't be searching for a command with spaces in its arguments -rinr() { - # shellcheck disable=SC2009 # pgrep has no fixed string option, plus see above. - if ps h -o args -C "${1##*/}" | grep -Fxqv "$*" &>/dev/null || [[ $? == 141 ]]; then - "$@" - fi -} -# variation of above: run or wait if running -rowir() { - local pid - pid=$(ps h -o 'pid,args' -C "${1##*/}" | sed -r 's/^[[:space:]]*([0-9]+)[[:space:]](.*)/\1\n\2/' | grep -B1 -Fx "$*" | head -n1 ||: ) - if [[ $pid ]]; then - # https://unix.stackexchange.com/questions/427115/listen-for-exit-of-process-given-pid - tail --pid="$pid" -f /dev/null - else - "$@" - fi -} - -mpvrpc-loadfile() { - local path nextpath cachedir finalpath nextpath count - cachedir=$HOME/.iank-music-cache - path="$1" - nextpath="$2" - - # note: logic duplicated in beetpull - local remote_p=true - source /p/c/domain-info - if [[ $HOSTNAME == "$d_host" ]]; then - remote_p=false - fi - - if $remote_p; then - finalpath="$cachedir${path#/i/m}" - rowir rsync --partial -a --inplace --mkpath "b8.nz:$path" "$finalpath" - finalnextpath="$cachedir${nextpath#/i/m}" - count=$(pgrep -a -f "^rsync --partial -a --inplace --mkpath $cachedir" || [[ $? == 1 ]] ) - # allow us to start 2 rsyncs in the background - if [[ $count == [01] ]]; then - rinr rsync --partial -a --inplace --mkpath "b8.nz:$nextpath" "$finalnextpath" & - fi - else - finalpath="$path" - fi - mpvrpc '{ "command": ["loadfile", "'"$finalpath"'"] }' -} +# I tried making it a separate script, but the background process +# management stuff doesn't work that way. There might be a fix, but I +# didn't try to find one. +. /a/c/beetag # usage: FILE|ALBUM_DIR [GENRE] beetadd() { diff --git a/fsf-script-lib b/fsf-script-lib index ddcfce9..32dcdfb 100644 --- a/fsf-script-lib +++ b/fsf-script-lib @@ -52,6 +52,26 @@ int_regex='^-?[0-9]+$' uint_regex='^[0-9]+$' +# horizontal row. used to break up output +hr() { + local start end end_count arg + # 180 is long enough. 5 for start. + start=█████ end=█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ + end_count=$(( ${COLUMNS:-180} - 5 )) + arg="$*" + if [[ $arg ]]; then + end_count=$(( end_count - 2 - ${#arg} )) + start="$start $arg " + fi + if (( end_count >= 1 )); then + end=${end:0:$end_count} + else + + end= + fi + printf "%s\n" "$(tput setaf 5 2>/dev/null ||:)$start$end$(tput sgr0 2>/dev/null||:)" +} + rm-maybe() { if (( $# )); then rm -f "$@" -- 2.30.2