#!/bin/bash
-f=/usr/local/lib/err;test -r $f || { echo "error: $0 no $f" >&2;exit 1;}; . $f
+set -e; . /usr/local/lib/err; 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
m ()
{
printf "%s\n" "$*" >&2
- if $dry_run && [[ $1 == beet && $2 == modify ]]; then
+ if $dry_run; then
echo "dry run: $*"
else
"$@"
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}"
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
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
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