wip refactor beetag
authorIan Kelling <ian@iankelling.org>
Sat, 1 Mar 2025 21:14:46 +0000 (16:14 -0500)
committerIan Kelling <ian@iankelling.org>
Sat, 1 Mar 2025 21:14:46 +0000 (16:14 -0500)
beetag

diff --git a/beetag b/beetag
index e786bc19491e6ee0fcbdeccbbb570fc5f50b9aff..bd9b98b9cf7cbf8c4800747ae7655c577aca910c 100755 (executable)
--- a/beetag
+++ b/beetag
@@ -110,6 +110,18 @@ rowir() {
   fi
 }
 
+# On slow systems, ~.3 seconds before mpv is ready after starting. This
+# impatiently waits.
+mpvrpc-wait-idle() {
+  local -i i
+  for (( i=0; i<20; i++ )); do
+    if [[ $(mpvrpco '{ "command": ["get_property", "idle-active"] }' 2>/dev/null | jq .data) == true ]]; then
+      return
+    fi
+    sleep .1
+  done
+
+}
 
 mpvrpc-loadfile() {
   local path nextpath cachedir finalpath nextpath count
@@ -183,9 +195,9 @@ mpvrpc-percent-pos() {
 #
 # Sets variables: pl_state_path pl_seed_path. Does mkdir of their directory.
 #
-# Creates/recreates $pl_seed_path if needed.
+# Creates/recreates file $pl_seed_path if needed.
 #
-# Input vars: random
+# Input vars: random, new_random
 pl-state-init() {
   local seed_num seed_file pl_state_dir pl_state_file
   # note: this structure of files is rather haphazard.
@@ -207,6 +219,7 @@ pl-state-init() {
 
   pl_state_path=$pl_state_dir/$pl_state_file
   pl_seed_path=$pl_state_dir/$seed_file
+  readonly pl_state_path pl_seed_path
 
   if $new_random || [[ ! -r $pl_seed_path ]]; then
     { base64 < /dev/urandom | head -c 200 ||:; echo; } > $pl_seed_path
@@ -236,7 +249,7 @@ toggle-rare-genres() {
     button_i[${buttons[i]}]=$i
   done
 
-  }
+}
 
 # Call from beetag. Queries our songlist and parses it into variables.
 #
@@ -274,7 +287,30 @@ beetag-ls-setup() {
     ls_lines+=("$ls_line")
   done
 
-  }
+}
+
+# Output most of 1 page worth of playlist entries near the current song.
+#
+# I only care to see a smallish portion of the list when starting.
+#
+# Input vars: j, id_count, ls_lines
+beetag-head-playlist() {
+  local head_count head_start ls_line
+  local -i i
+  head_count=$(( LINES - 20 ))
+  head_start=$(( j - head_count / 2 ))
+  if (( head_start < 0 )); then
+    head_start=0
+  fi
+  for (( i=head_start; i < head_count && i < id_count; i++ )); do
+    ls_line="${ls_lines[$i]}"
+    if (( i == j )); then
+      echo "* $ls_line"
+    else
+      echo "$ls_line"
+    fi
+  done
+}
 
 # tag with beets.
 # usage: beetag [-r] [-s] QUERY
@@ -298,22 +334,43 @@ beetag-ls-setup() {
 # todo: enter should also unpause
 beetag()  {
   source /a/bin/ds/beet-data
-  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
+
+  # state vars altered by user input:
+  local char do_rare_genres last_genre_i doplay=true repeat1=false
+  local -i volume=70 read_wait=2 j=0
+  local -a button_map
   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
 
-  first_play=true
-  erasable_line=false
-  escape_char=$(printf "\u1b")
-  scrolled=999 # more than any $LINES
+  # cli options / args
+  local new_random random
+  local -a beet_query
+
+  # constants
+  local escape_char pl_state_path pl_seed_path
+  local -ar buttons=( {a..p} {r..w} {6..8} , . / - "=")
+  local -r skip_input_regex="^[0-9]+$"
+
+  # song list vars:
+  local -a ids ls_lines paths
+  local -i id_count
+
+  # current song vars:
+  local id ls_out path
+  local -a tags
+
+  # boolean state vars
+  local expected_input remove first_play=true erasable_line=false
+
+  # iterator vars
+  local ls_line tag
+
+  # misc vars
+  local char_i escaped_input new_item seek_sec
+  local -i scrolled=999 # more than any $LINES
+  local -i i ret line_int skip_start pre_j_count skip_lookback overflow_lines overflow
+
   ### begin arg processing ###
   random=false
-  repeat1=false
   new_random=false
   case $1 in
     -r)
@@ -336,87 +393,38 @@ beetag()  {
   beet_query=("$@")
   ### end arg processing ###
 
-  readonly -a buttons=( {a..p} {r..w} {6..8} , . / - "=")
+  escape_char=$(printf "\u1b")
+  readonly escape_char
 
-  ### begin control knob vars ###
-  volume=70
-  doplay=true
-  ### end control knob vars ###
 
-  read_wait=2
   toggle-rare-genres
   pl-state-init
   beetag-ls-setup
 
-  j=0
-  if [[ $playlist ]]; then
-    if [[ -r $pl_state_path ]]; then
-      j=$(cat $pl_state_path)
-    fi
+  if [[ $playlist && -r $pl_state_path ]]; then
+    j=$(cat $pl_state_path) # playlist position.
   fi
 
-  # i only care to see a smallish portion of the list when starting.
-  head_count=$(( LINES - 20 ))
-  head_start=$(( j - head_count / 2 ))
-  if (( head_start < 0 )); then
-    head_start=0
-  fi
-  for (( i=head_start; i < head_count && i < id_count; i++ )); do
-    ls_line="${ls_lines[$i]}"
-    if (( i == j )); then
-      echo "* $ls_line"
-    else
-      echo "$ls_line"
-    fi
-  done
-  if $doplay; then
-    #{ mpv --profile=a --volume=$volume --idle 2>&1 & } 2>/dev/null
-    mpv --profile=a --volume=$volume --idle &
-    # if we dont sleep, can expect an error like this:
-    # socat[1103381] E connect(5, AF=1 "/tmp/mpvsock", 14): Connection refused
-    sleep .1
-  fi
+  beetag-head-playlist
+
+  #{ mpv --profile=a --volume=$volume --idle 2>&1 & } 2>/dev/null # todo: consider using again
+  mpv --profile=a --volume=$volume --idle &
+  sleep .1 # or else: socat[1103381] E connect(5, AF=1 "/tmp/mpvsock", 14): Connection refused
 
   while true; do
     id=${ids[j]}
     path="${paths[$j]}"
-    lsout="${ls_lines[j]}"
-    tags=( ${lsout%%,*} )
+    ls_out="${ls_lines[j]}"
+    tags=( ${ls_out%%,*} )
     beetag-help
-    printf "██ %s\n" "$lsout"
+    printf "██ %s\n" "$ls_out"
     beetag-nostatus 1
     if $doplay; then
-      # https://stackoverflow.com/a/7687716
-      # note: duplicated down below
-      #
-      # notes on old method of invoking mpv each time:
-      # https://superuser.com/questions/305933/preventing-bash-from-displaying-done-when-a-background-command-finishes-execut
-      # we can't disown or run in a subshell or set +m because all that
-      # disabled job control from working properly in ways we want.
-      # todo: figure out some kind of answer to this. I think the solution
-      # is that we are waiting in 2 second intervals and checking if the
-      # background job exists. Instead, we should make mpv just idle
-      # when it is done with a song and then send it a command to play a new track.
-      #{ mpv --profile=a --volume=$volume "$path" 2>&1 & } 2>/dev/null
-      # old
-      #{ beet play "--args=--volume=$volume" "id:$id" 2>&1 & } 2>/dev/null
-
-      # on slow systems, we may need to wait like .3 seconds before mpv
-      # is ready. so impatiently check until it is ready
-      if $first_play; then
-        first_play=false
-        for (( i=0; i<20; i++ )); do
-          if [[ $(mpvrpco '{ "command": ["get_property", "idle-active"] }' 2>/dev/null | jq .data) == true ]]; then
-            mpvrpc-loadfile "$path"
-            break
-          fi
-          sleep .1
-        done
-      else
-        mpvrpc-loadfile "$path"
-      fi
+      if $first_play; then first_play=false; mpvrpc-wait-idle; fi
+      mpvrpc-loadfile "$path"
       erasable_line=false
     fi
+
     while true; do
       char=
       if $doplay; then
@@ -536,7 +544,6 @@ beetag()  {
         "$escape_char")
           expected_input=true
           read -rsn2 escaped_input
-          skip_input_regex="^[0-9]+$"
           case $escaped_input in
             # up char: show all the songs, use less
             '[A')
@@ -632,12 +639,9 @@ beetag()  {
         m beetmq "id:$id" genre=$new_item
       else
         remove=false
-        tmp_tags=()
         for tag in ${tags[@]}; do
           if [[ $new_item == "$tag" ]]; then
             remove=true
-          else
-            tmp_tags+=("$tag")
           fi
         done
         if $remove; then