host info updates
[distro-setup] / nav2beet
index b0ffcd5698a294086c69d0cf06846ca7b104528a..e7bdec0b4641e2c8ed7cc09a32b81163f1f64862 100755 (executable)
--- a/nav2beet
+++ b/nav2beet
@@ -1,17 +1,52 @@
 #!/bin/bash
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to switch
+# its license to GPL.
 
-f=/usr/local/lib/err;test -r $f || { echo "error: $0 no $f" >&2;exit 1;}; . $f
+# Copyright 2024 Ian Kelling
 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
 
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+set -e; . /usr/local/lib/bash-bear; set +e
+
+source /a/bin/ds/beet-data
+source /b/bash_unpublished/source-semi-priv
+
+declare -A genre_a
+for g in ${all_genres[@]}; do
+  genre_a[$g]=t
+done
+
+# these options are mainly for debugging / developing quickly.
 plist_only=false
 dry_run=false
 while (( $# )); do
   case $1 in
     plist)
       plist_only=true
+      shift
       ;;
     dry)
       dry_run=true
+      shift
+      ;;
+    *)
+      echo "error. unexpected arg read script"
+      exit 1
       ;;
   esac
 done
@@ -19,7 +54,7 @@ done
 m ()
 {
   printf "%s\n" "$*" >&2
-  if $dry_run && [[ $1 == beet && $2 == modify ]]; then
+  if $dry_run; then
     echo "dry run: $*"
   else
     "$@"
@@ -28,37 +63,34 @@ m ()
 
 tmpf=$(mktemp)
 
+declare -A flacs
 
-if ! $plist_only; then
-  echo begin star rating import from navidrome to beets
+declare -A navirating
+tmpdir=$(mktemp -d)
+cd $tmpdir
+# the code here is duplicated later for non ssh context.
+if [[ $HOSTNAME != kd ]]; then
 
-  declare -A navirating
-  tmpdir=$(mktemp -d)
-  cd $tmpdir
-  if [[ $HOSTNAME != kd ]]; then
-    ssh_prefix="ssh b8.nz"
-    ssh b8.nz '
+  ssh b8.nz bash -s "$(md5sum </a/bin/ds/beet-data)" <<'EOF'  | tar xz
+if [[ $1 != "$(md5sum </a/bin/ds/beet-data)" ]]; then
+  echo error: old beet-data on kd
+  exit 1
+fi
 install -m 700 -d /tmp/nav2beet
-for r in 1 2 3 4 5; do
- sqlite3 /i/navidrome/navidrome.db ".output /tmp/nav2beet/$r" "select path from annotation inner join media_file on item_id = id where rating = $r;"
-done
+cd /tmp/nav2beet
+/a/bin/ds/nav2beet-local
 tar cz -C /tmp nav2beet
-' | tar xz
-    cd nav2beet
-  else
-    for r in 1 2 3 4 5; do
-      sqlite3 /i/navidrome/navidrome.db ".output $r" "select path from annotation inner join media_file on item_id = id where rating = $r;"
-    done
-  fi
-  declare -A flacs
-
-  # todo: consider if this is a problem: file removed/renamed in main
-  # collection, but not yet updated navidrome, we want to skip it not
-  # die.
+EOF
+  cd nav2beet
+else
+  /a/bin/ds/nav2beet-local
+fi
+while read -r l; do
+  flacs[$l]=t
+done <flacs
 
-  while read -r l; do
-    flacs[$l]=t
-  done < <($ssh_prefix find /i/m -type f -name '*.flac')
+if ! $plist_only; then
+  echo begin star rating import from navidrome to beets
   for r in 1 2 3 4 5; do
     while read -r path; do
       beetpath="/i/m${path#/i/converted}"
@@ -69,10 +101,9 @@ tar cz -C /tmp nav2beet
       navirating[$beetpath]=$r
     done <$r
   done
-  cd
-  rm -rf $tmpdir
   declare -A beetrating
   for r in 1 2 3 4 5; do
+    # shellcheck disable=SC2016 # expected beets arg
     m beet ls -f '$path' rating:$r >$tmpf
     while read -r path; do
       beetrating[$path]=$r
@@ -81,8 +112,16 @@ tar cz -C /tmp nav2beet
 
   for path in "${!navirating[@]}"; do
     r="${navirating[$path]}"
+    if [[ ! "${beetrating[$path]}" ]]; then
+      if [[ -e $path ]]; then
+        echo "$0: ERROR: $path exists but we have no rating for it"
+        exit 1
+      else
+        echo "$0: WARNING: $path exists in navidrome but not beets"
+        continue
+      fi
+    fi
     if [[ $r != "${beetrating[$path]}" ]]; then
-      # note: this assumes there are no cases like filea.mp3 filea.mp3.mp3, which would affect both files.
       echo "$r != ${beetrating[$path]}, beet modify -y path:$path rating=$r"
       beet modify -y "path:$path" "rating=$r"
     fi
@@ -91,74 +130,67 @@ tar cz -C /tmp nav2beet
 fi
 
 echo begin import navidrome playlists as flexible attribute with value t.
-# These are only the playlists listed in the beets config.yaml
-# "subsonicplaylist:" and then duplicated here:
-
-
-plists=(
-  # these are useful tags
-  expl
-  gimicky
-  sad
-  # these are normal playlists
-  love
-  pump1
-  pumprap
-  rend
-  run
-)
-# this puts the navidrome playlists into a smart attribute
-# subsonic_playlist
-m beet subsonicplaylist
-
-beet ls -f '$id $subsonic_playlist' subsonic_playlist::. | sed 's/;/ /g' >$tmpf
-
-# for debugging
-#m head $tmpf
-
-navlists=()
-while read -r id id_plists; do
-  navlists[id]="$id_plists"
-done <$tmpf
-
-for plist in ${plists[@]}; do
-  echo "processing $plist"
-  for id in $(beet ls -f '$id' $plist:t ^genre:spoken-w ^genre:skit ^rating:1); do
-    found=false
-    newnavlist=()
-    for navlist in ${navlists[id]}; do
-      if [[ $navlist == "$plist" ]]; then
-        found=true
-      else
-        newnavlist+=($navlist)
-      fi
-    done
-    if $found; then
-      navlists[id]="${newnavlist[*]}"
+
+shopt -s nullglob
+for path in 2genre/*/*; do
+  id="${path#*/}"
+  id="${id%/*}"
+  filename="${path##*/}"
+  tmp="${filename%%[^0-9-]*}"
+  genre="${filename#"$tmp"}"
+  if [[ ${genre_a[$genre]} ]]; then
+    is_genre=true
+  else
+    # Some playlists we create with random names to remind us to do something
+    # with these tracks later once we are at a computer.
+    is_genre=false
+  fi
+  while read -r path; do
+    flac="${path%.mp3}.flac"
+    if [[ ${flacs[$flac]} ]]; then
+      path="$flac"
+    fi
+    if $is_genre; then
+      m beet modify -y "path:$path" "genre=$genre"
     else
-      # exists in beets, but not navidrome so we must have removed
-      # it from the playlist in navidrome.
-      m beet modify -y "id:$id" "$plist!"
+      m beet modify -y "path:$path" "$genre=t"
     fi
-  done
-done
-# The ones we didnt find and remove are ones we added
-# in navidrome.
-for id in ${!navlists[@]}; do
-  [[ ${navlists[id]} ]] || continue
-  m beet modify -y "id:$id" ${navlists[id]// /=t }=t
+  done <"$path"
+  # how i figured this out:
+  # s tcpdump -i any -w /tmp/tcpdump port 4533
+  # then delete a test playlist in client.
+  # made some sense out of it with: http://www.subsonic.org/pages/api.jsp
+  # open file in wireshard, right click "Hypertext Transfer Protocol", copy, as printable text, put into file /tmp/headers
+  # /a/opt/h2c/h2c </tmp/headers
+  # change from https to http.
+  # then tested out removing stuff i suspected was not important,
+  # and added https and host so it would work remotely.
+  m curl --http1.1 --user "iank:$navidrome_pw" "https://b8.nz/rest/deletePlaylist.view?u=iank&s=sb219dvv7egnoe4i47k75cli0m&t=1c8f5575cd0fdf03deb971187c9c88b1&v=1.2.0&c=DSub&id=$id"
 done
 
-# old way of doing it which sets everything. this doesnt
-# account for removed tracks in navidrome, and it will
-# be slower for handling many playlists.
-# while read -r id plists; do
-#   plists="${plists#;}"
-#   m beet modify -y "id:$id" ${plists//;/=t }
-# done < <(beet ls -f '$id $subsonic_playlist' subsonic_playlist::.)
 
-# if we remove a track from a playlist in navidrome,
-# beet subsonicplaylist won't remove corresponding beet
-# smart attribute, so clear them all here.
+for plist in ${nav_tags[@]}; do
+  echo "processing $plist"
+  # shellcheck disable=SC2016 # expected beets arg
+  beet ls -f '$path' $plist:t $nav_convert_query | sort | sed 's,\.flac$,.mp3,'> p
+  while read -r path; do
+    flac="${path%.mp3}.flac"
+    if [[ ${flacs[$flac]} ]]; then
+      path="$flac"
+    fi
+    m beet modify -y "path:$path" "$plist!"
+    # files unique to tmpf are in beets not navidrome
+  done < <(comm -23 p $plist)
+  while read -r path; do
+    flac="${path%.mp3}.flac"
+    if [[ ${flacs[$flac]} ]]; then
+      path="$flac"
+    fi
+    m beet modify -y "path:$path" $plist=t
+    # files unique to plist are in navidrome not beets
+  done < <(comm -13 p $plist)
+
+done
 
-m beet modify -y subsonic_playlist::. 'subsonic_playlist!'
+cd
+rm -rf $tmpdir