# Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): y
[sendemail]
confirm = auto
+# https://stackoverflow.com/questions/70663523/the-unauthenticated-git-protocol-on-port-9418-is-no-longer-supported
+[url "https://github.com/"]
+ insteadOf = git://github.com/
--- /dev/null
+#!/bin/bash
+
+# for generating playlist config yaml
+
+f=/usr/local/lib/err;test -r $f || { echo "error: $0 no $f" >&2;exit 1;}; . $f
+
+declare -A ignore_genres_a
+ignore_genres=(
+ skit
+ spoken-w
+)
+
+declare -A slow_genres_a
+slow_genres=(
+ ambient
+ avantg
+ classical
+ noise
+)
+
+tags=(
+ expl
+ sad
+)
+
+for g in ${ignore_genres[@]}; do
+ ignore_genres_a[$g]=t
+done
+for g in ${slow_genres[@]}; do
+ slow_genres_a[$g]=t
+done
+
+# genres that have a beat
+beat_genres=()
+
+# generate genres based on what is in the db.
+genres=()
+for g in $(beet ls -f '$genre' | sort -u); do
+ if [[ ${ignore_genres_a[$g]} ]]; then continue; fi
+ genres+=($g)
+ if [[ ${slow_genres_a[$g]} ]]; then continue; fi
+ beat_genres+=($g)
+done
+
+# generate regex for beat playlist
+beat_regex=
+first=true
+for g in ${beat_genres[@]}; do
+ if $first; then
+ first=false
+ beat_regex=$g
+ else
+ beat_regex+="|$g"
+ fi
+done
+
+
+for g in ${genres[@]}; do
+ for r in {3..5}; do
+ case $g in
+ pop|rap)
+ cat <<EOF
+ - name: ${g}-${r}.m3u
+ query: 'rating:${r}..5 genre:$g ^expl:t'
+ - name: ${g}e-${r}.m3u
+ query: 'rating:${r}..5 genre:$g'
+EOF
+ ;;
+ *)
+ cat <<EOF
+ - name: ${g}-${r}.m3u
+ query: 'rating:${r}..5 genre:$g'
+EOF
+ ;;
+ esac
+ done
+done
+
+for t in ${tags[@]}; do
+ for r in {3..5}; do
+ cat <<EOF
+ - name: ${t}-${r}.m3u
+ query: 'rating:${r}..5 $t:t'
+EOF
+
+ done
+done
+
+for r in {3..5}; do
+ cat <<EOF
+ - name: beat-${r}.m3u
+ query: 'rating:${r}..5 genre::$beat_regex ^expl:t'
+ - name: beate-${r}.m3u
+ query: 'rating:${r}..5 genre::$beat_regex'
+EOF
+done
fi
+
+
mysrc() {
local path dir file
path=$1
# we have ~33 buttons as of this writing, so lets
# prune down the history every once in a while.
if (( start > 500 )); then
- tac ~/.cdirs | awk '!seen[$0]++' | head -n 200 | sponge ~/.cdirs
+ tac ~/.cdirs | awk '!seen[$0]++' | head -n 200 | tac | sponge ~/.cdirs || [[ $? == 141 ]]
fi
for (( j=$start; j >= 0; j-- )); do
fi
read -r -N 1 input
if [[ $input != $'\n' ]]; then
- c ${buttondirs[$input]}
+ c "${buttondirs[$input]}"
fi
}
# back list
}
-# for running in a fai rescue
+# for running in a fai rescue. iank specific.
kdrescue() {
d=vgata-Samsung_SSD_850_EVO_2TB_S2RLNX0J502123D
for f in $d vgata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V; do
}
+chownme() {
+ s chown -R $USER:$USER "$@"
+}
+
# shellcheck disable=SC2032
chown() {
# makes it so chown -R symlink affects the symlink and its target.
done
}
+# df progress
+# usage: dfp MOUNTPOINT [SECOND_INTERVAL]
+# SECOND_INTERVAL defaults to 90
+dfp() {
+ # mp = mountpoint
+ local a b mp interval
+ mp=$1
+ interval=${2:-90}
+ if [[ ! $mp ]]; then
+ echo "dfp: error, missing 1st arg" >&2
+ return 1
+ fi
+ while true; do
+ a=$(df --output=used $mp | tail -n1)
+ sleep $interval
+ b=$(df --output=used $mp | tail -n1)
+ printf "used mib: %'d mib/min: %s\n" $(( b /1000 )) $(( (b-a) / (interval * 1000 / 60 ) ))
+ done
+}
+
# get ipv4 ip from HOST. or if it is already a number, return that
hostip() {
local host="$1"
printf "%s" "${arg}" |& hexdump -C
done
}
-# echo vars. print var including escapes, etc
+
+# echo variables. print var including escapes, etc, like xxd for variable
ev() {
if (( ! $# )); then
echo no args
/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
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
# 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
-pre="${0##*/}: "
+script_name="${BASH_SOURCE[0]}"
+script_name="${script_name##*/}"
+pre="${SSH_CLIENT:+$HOSTNAME} $script_name:"
m() { if $verbose; then printf "$pre%s\n" "$*"; fi; "$@"; }
e() { printf "$pre%s\n" "$*"; }
die() { printf "$pre%s\n" "$*" >&2; echo "exiting with status 1" >&2; exit 1; }
sleep 5
fi
+targets=()
early=false
cron=false
orig_args=("$@")
-temp=$(getopt -l cron,pull-reexec,help ceil:m:npqrs:t:vh "$@") || usage 1
+temp=$(getopt -l cron,pull-reexec,help 23ceil:m:npqrs:t:vh "$@") || usage 1
eval set -- "$temp"
while true; do
case $1 in
--cron)
cron=true
pre=
- shift
;;
+ # for the rare case we want to run multiple instances at the same time
+ -2) conf_suf=2 ;;
+ -3) conf_suf=3 ;;
# only creates the config file, does not run btrbk
- -c) conf_only=true; shift ;;
+ -c) conf_only=true ;;
# quit early, just btrbk, no extra remounting etc.
- -e) early=true; shift ;;
- -i) incremental_strict=true; shift ;;
+ -e) early=true ;;
+ -i) incremental_strict=true ;;
# bytes per second, suffix k m g
- -l) rate_limit=$2; shift 2 ;;
+ -l) rate_limit=$2; shift ;;
# Comma separated mountpoints to backup. This has defaults set below.
- -m) IFS=, mountpoints=($2); unset IFS; shift 2 ;;
- -n) dry_run=true; dry_run_arg=-n; shift ;;
+ -m) IFS=, mountpoints=($2); unset IFS; shift ;;
+ -n) dry_run=true ;;
# hide progress
- -p) progress_arg=; shift ;;
+ -p) progress_arg= ;;
# internal option for rerunning under newer SOURCE_HOST version.
- --pull-reexec) pull_reexec=true; shift ;;
+ --pull-reexec) pull_reexec=true;;
# quiet
- -q) verbose=false; verbose_arg=; progress_arg=; shift ;;
+ -q) verbose=false; verbose_arg=; progress_arg= ;;
# source host to receive a backup from
-s)
source=$2
if [[ $source == *:* ]]; then
bbksource="[$source]"
fi
- shift 2
+ shift
;;
# target hosts to send to. empty is valid for just doing local
# snapshot. we have default hosts we will populate.
- -t) IFS=, targets=($2); unset IFS; shift 2 ;;
+ -t) IFS=, targets=($2); unset IFS; shift ;;
# verbose.
- -v) verbose=true; verbose_arg=-v; shift ;;
+ -v) verbose=true; verbose_arg=-v ;;
-h|--help) usage ;;
--) shift; break ;;
*) die "Internal error!" ;;
esac
+ shift
done
cmd_arg=${1:-run}
std_preserve="36h 14d 8w 24m"
-q_preserve="18h 14d"
+q_preserve="18h 14d 8w"
case $cmd_arg in
run|resume) : ;;
fi
if $verbose; then
- printf "options: conf_only=%s\ndry_run=%s\nrate_limit=%s\nverbose=%s\ncmd_arg=%s" "$conf_only" "$dry_run" "$rate_limit" "$verbose" "$cmd_arg"
+ printf "$pre options: conf_only=%s\ndry_run=%s\nrate_limit=%s\nverbose=%s\ncmd_arg=%s" "$conf_only" "$dry_run" "$rate_limit" "$verbose" "$cmd_arg"
fi
### end options parsing
kd_spread=false
at_work=false
+ at_home=false
+
+ case $HOSTNAME in
+ kw|kd|frodo|x2|x3|sy) : ;;
+ *)
+ die "error: no default targets for this host, use -t"
+ ;;
+ esac
- # todo, fix this up once frodo is back
- # targets=(frodo.b8.nz)
case $HOSTNAME in
kw)
at_work=true
;;&
- x2|x3|sy|bo)
- if ping -q -c1 -w1 hal.office.fsf.org \
+ kd|frodo)
+ at_home=true
+ ;;&
+ x2|x3|sy)
+ if [[ $(dig +short @10.2.0.1 -x 10.2.0.2 2>&1 ||:) == kd.b8.nz. ]] \
+ && ip n show 10.2.0.1 | grep . &>/dev/null; then
+ at_home=true
+ elif ping -q -c1 -w1 hal.office.fsf.org &>/dev/null \
&& ip n show 192.168.0.26 | grep . &>/dev/null; then
at_work=true
fi
;;&
- kw|x2|x3|sy|bo)
- if $at_work; then
- if ping -q -c1 -w1 iank.vpn.office.fsf.org &>/dev/null; then
- home=iank.vpn.office.fsf.org
- else
- home=i.b8.nz
- fi
- else
- if ping -q -c1 -w1 b8.nz &>/dev/null; then
- home=b8.nz
+ *)
+ if $at_home; then
+ # main work machine
+ if ping -q -c1 -w1 x3.office.fsf.org &>/dev/null; then
+ targets+=(x3.office.fsf.org)
else
- home=i.b8.nz
+ targets+=(x3wg.b8.nz)
fi
- fi
- ;;&
- x2)
- targets+=($home)
- ;;
- kw)
- targets+=($home x2.office.fsf.org)
- ;;
- x3|sy|bo)
- targets+=($home)
- if $at_work; then
- targets+=(x2.office.fsf.org x2.b8.nz)
- else
- targets+=(x2wg.b8.nz)
- fi
- ;;
- kd)
- if ! $kd_spread; then
- if ping -q -c1 -w1 x2.office.fsf.org &>/dev/null; then
- targets+=(x2.office.fsf.org)
+ for h in frodo kd; do
+ if [[ $HOSTNAME == "$h" ]]; then
+ continue
+ fi
+ targets+=($h.b8.nz)
+ done
+ for h in x2 x3 sy; do
+ if [[ $HOSTNAME == "$h" ]]; then
+ continue
+ fi
+ if ping -q -c1 -w1 $h.b8.nz &>/dev/null; then
+ targets+=($h.b8.nz)
+ elif ping -q -c1 -w1 ${h}w.b8.nz &>/dev/null; then
+ targets+=(${h}w.b8.nz)
+ fi
+ done
+ elif $at_work; then
+ if ping -q -c1 -w1 iank.vpn.office.fsf.org &>/dev/null; then
+ targets+=(iank.vpn.office.fsf.org)
else
- targets+=(x2wg.b8.nz)
+ targets+=(i.b8.nz)
fi
- fi
- if ping -q -c1 -w1 sy.b8.nz &>/dev/null; then
- targets+=(sy.b8.nz)
- else
- targets+=(syw.b8.nz)
- fi
- if ping -q -c1 -w1 x3.b8.nz &>/dev/null; then
- targets+=(x3.b8.nz)
+ for h in x2 x3 kw; do
+ if [[ $HOSTNAME == "$h" ]]; then
+ continue
+ fi
+ if ping -q -c1 -w1 $h.office.fsf.org &>/dev/null; then
+ targets+=($h.office.fsf.org)
+ fi
+ done
else
- targets+=(x3w.b8.nz)
+ targets+=(i.b8.nz)
fi
;;
- frodo)
- # no targets
- targets=()
- ;;
- *)
- die "error: no default targets for this host, use -t"
- ;;
esac
fi
prospective_mps=(/a)
else
case $HOSTNAME in
- frodo)
- prospective_mps=(/i)
- ;;
*)
prospective_mps=()
if [[ $source ]]; then
fi
# if our mountpoints are from stale snapshots,
# it doesn't make sense to do a backup.
-check-subvol-stale ${mountpoints[@]} || die "found stale mountpoints in ${mountpoints[*]}"
+m check-subvol-stale ${mountpoints[@]} || die "found stale mountpoints in ${mountpoints[*]}"
# for an initial run, btrbk requires the dir to exist.
mkdir -p /mnt/{root,o}/btrbk
fi
-cat >/etc/btrbk.conf <<EOF
+cat >/etc/btrbk$conf_suf.conf <<EOF
ssh_identity /q/root/h
#ssh_identity /root/.ssh/home
#stream_compress zstd
# so we only run one at a time
-lockfile /var/lock/btrbk.lock
+lockfile /var/lock/btrbk$conf_suf.lock
# default format of short does not accomidate hourly preservation setting
timestamp_format long-iso
EOF
if $incremental_strict; then
- cat >>/etc/btrbk.conf <<EOF
+ cat >>/etc/btrbk$conf_suf.conf <<EOF
incremental strict
EOF
fi
+qconf() {
+ case $sub in
+ q)
+ # q has sensitive data i dont want to backup for so long
+ cat >>/etc/btrbk$conf_suf.conf <<EOF
+snapshot_preserve $q_preserve
+snapshot_preserve_min 2h
+snapshot_dir btrbk
+target_preserve $q_preserve
+target_preserve_min 2h
+EOF
+ ;;
+ esac
+
+}
+
+# make /q be last
+mp_count=${#mountpoints[@]}
+for (( i=0; i < mp_count - 1 ; i++ )); do
+ if [[ ${mountpoints[i]} == /q ]]; then
+ unset mountpoints[i]
+ mountpoints+=(/q)
+ fi
+done
+
for m in ${mountpoints[@]}; do
case $m in
/o)
sub=${m#/}
if [[ $source ]]; then
- cat >>/etc/btrbk.conf <<EOF
+ cat >>/etc/btrbk$conf_suf.conf <<EOF
volume ssh://$bbksource$vol
subvolume $sub
+EOF
+ qconf
+ cat >>/etc/btrbk$conf_suf.conf <<EOF
target send-receive $vol/btrbk
EOF
fi
if (( ${#targets[@]} )); then
- cat >>/etc/btrbk.conf <<EOF
+ cat >>/etc/btrbk$conf_suf.conf <<EOF
volume $vol
subvolume $sub
EOF
- case $sub in
- q)
- # q has sensitive data i dont want to backup for so long
- cat >>/etc/btrbk.conf <<EOF
-snapshot_preserve $std_preserve
-snapshot_preserve_min 2h
-snapshot_dir btrbk
-target_preserve $std_preserve
-target_preserve_min 2h
-EOF
- ;;
- esac
+ qconf
for tg in ${targets[@]}; do
# handle ipv6
if [[ $tg == *:* ]]; then
tg="[$tg]"
fi
- cat >>/etc/btrbk.conf <<EOF
+ cat >>/etc/btrbk$conf_suf.conf <<EOF
target send-receive ssh://$tg$vol/btrbk
EOF
done
if $dry_run; then
- m btrbk -v -n $cmd_arg
+ m btrbk -c /etc/btrbk$conf_suf.conf -v -n $cmd_arg
mexit 0
fi
# -q and just using the syslog option seemed nice,
# but it doesn't show when a send has a parent and when it doesn't.
-m btrbk $preserve_arg $verbose_arg $progress_arg $cmd_arg
+m btrbk -c /etc/btrbk$conf_suf.conf $preserve_arg $verbose_arg $progress_arg $cmd_arg
+
+if $early; then
+ exit 0
+fi
# todo: tp not valid anymore.
# if we have it, sync to systems which don't
### begin docker install ####
if isdeb; then
- # https://store.docker.com/editions/community/docker-ce-server-debian?tab=description
+# https://docs.docker.com/engine/install/ubuntu/
+
+sudo mkdir -p /etc/apt/keyrings
+
+ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
+
+# lsb_release -cs -> debian-codename-compat
+echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(debian-codename-compat) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+
+p update
+
+
pi software-properties-common apt-transport-https
curl -fsSL https://download.docker.com/linux/$(distro-name-compat)/gpg | sudo apt-key add -
url=https://download.docker.com/linux/$(distro-name-compat)
--- /dev/null
+# this was in distro-end.
+#
+# Disabled because basically I don't need a home vpn and prefer just
+# port forwarding as needed.
+
+### begin home vpn server setup
+
+
+# # this section done initially to make persistent keys.
+# # Also note, I temporarily set /etc/hosts so my host was
+# # b8.nz when running this, since the vpn client config
+# # generator assumes we need to go to that server to get
+# # server keys.
+# vpn-server-setup -rds
+# s cp -r --parents /etc/openvpn/easy-rsa/keys /p/c/filesystem
+# s chown -R 1000:1000 /p/c/filesystem/etc/openvpn/easy-rsa/keys
+# # kw = kgpe work machine.
+# for host in x2 x3 kw; do
+# vpn-mk-client-cert -b $host -n home b8.nz 1196
+# dir=/p/c/machine_specific/$host/filesystem/etc/openvpn/client
+# mkdir -p $dir
+# s bash -c "cp /etc/openvpn/client/home* $dir"
+# # note: /etc/update-resolv-conf-home also exists for all systems with /p
+# done
+
+# key already exists, so this won't generate one, just the configs.
+# m vpn-server-setup -rds
+# sudo tee -a /etc/openvpn/server/server.conf <<'EOF'
+# push "dhcp-option DNS 10.0.0.1"
+# push "route 10.0.0.0 255.255.0.0"
+# client-connect /a/bin/distro-setup/vpn-client-connect
+# EOF
+# sudo sed -i --follow-symlinks 's/10.8./10.9./g;s/^\s*port\s.*/port 1196/' /etc/openvpn/server/server.conf
+
+# if [[ $HOSTNAME == tp ]]; then
+# if [[ -e /lib/systemd/system/openvpn-server@.service ]]; then
+# vpn_service=openvpn-server@server
+# else
+# vpn_service=openvpn@server
+# fi
+# sgo $vpn_service
+# fi
+### end vpn server setup
set +x
source /a/bin/distro-functions/src/identify-distros
$interactive || set -x
-for f in kd x2 x3 frodo tp li bk je demohost kw sy bo; do
+for f in kd x2 x3 x8 frodo tp li bk je demohost kw sy bo; do
eval "$f() { [[ $HOSTNAME == $f ]]; }"
done
codename=$(debian-codename)
fi
fi
;;
+ frodo)
+ tu /etc/fstab <<'EOF'
+/dev/mapper/crypt_dev_ata-ata-Hitachi_HDS722020ALA330_JK1121YAG7SXWS-part1 /i btrfs nofail,x-systemd.device-timeout=30s,x-systemd.mount-timeout=30s,noatime,subvol=i 0 0
+EOF
+ if ! mountpoint /i &>/dev/null; then
+ sudo mkdir -p /i
+ if [[ -d /mnt/i/i ]]; then
+ sudo mount /i
+ fi
+ fi
+ ;;
esac
if bitfolk; then
client-to-client
EOF
- # sullivan d8
- sd /etc/openvpn/client-config-hole/sd8 <<'EOF'
-ifconfig-push 10.5.5.41 255.255.255.0
-EOF
- # hsieh d8
- sd /etc/openvpn/client-config-hole/hd8 <<'EOF'
-ifconfig-push 10.5.5.42 255.255.255.0
-EOF
-
- sd /etc/openvpn/client-config-hole/onep9 <<'EOF'
-ifconfig-push 10.5.5.14 255.255.255.0
+ sd /etc/openvpn/client-config-hole/kd <<'EOF'
+ifconfig-push 10.5.5.2 255.255.255.0
EOF
- sd /etc/openvpn/client-config-hole/bo <<'EOF'
-ifconfig-push 10.5.5.13 255.255.255.0
+ sd /etc/openvpn/client-config-hole/tp <<'EOF'
+ifconfig-push 10.5.5.3 255.255.255.0
EOF
- sd /etc/openvpn/client-config-hole/sy <<'EOF'
-ifconfig-push 10.5.5.12 255.255.255.0
+ sd /etc/openvpn/client-config-hole/frodo <<'EOF'
+ifconfig-push 10.5.5.5 255.255.255.0
EOF
- sd /etc/openvpn/client-config-hole/kw <<'EOF'
-ifconfig-push 10.5.5.9 255.255.255.0
+ sd /etc/openvpn/client-config-hole/x2 <<'EOF'
+ifconfig-push 10.5.5.7 255.255.255.0
EOF
sd /etc/openvpn/client-config-hole/x3 <<'EOF'
ifconfig-push 10.5.5.8 255.255.255.0
EOF
- sd /etc/openvpn/client-config-hole/x2 <<'EOF'
-ifconfig-push 10.5.5.7 255.255.255.0
-EOF
- sd /etc/openvpn/client-config-hole/wclient <<'EOF'
-ifconfig-push 10.5.5.6 255.255.255.0
+ sd /etc/openvpn/client-config-hole/kw <<'EOF'
+ifconfig-push 10.5.5.9 255.255.255.0
EOF
- sd /etc/openvpn/client-config-hole/frodo <<'EOF'
-ifconfig-push 10.5.5.5 255.255.255.0
+ sd /etc/openvpn/client-config-hole/sy <<'EOF'
+ifconfig-push 10.5.5.12 255.255.255.0
EOF
- sd /etc/openvpn/client-config-hole/amy <<'EOF'
-ifconfig-push 10.5.5.3 255.255.255.0
+ sd /etc/openvpn/client-config-hole/bo <<'EOF'
+ifconfig-push 10.5.5.13 255.255.255.0
EOF
- sd /etc/openvpn/client-config-hole/kd <<'EOF'
-ifconfig-push 10.5.5.2 255.255.255.0
+ sd /etc/openvpn/client-config-hole/onep9 <<'EOF'
+ifconfig-push 10.5.5.14 255.255.255.0
EOF
+ # todo: add x8?
+
- # for adding to current system:
- #vpn-mk-client-cert -s "" -n hole 72.14.176.105
- # adding to remove system 107,
- #vpn-mk-client-cert -s "" -n hole -c 10.2.0.107 -b hd8 iankelling.org
+ # for adding cert to system with /p
+ #
+ # host=frodo
+ #mkc /p/c/machine_specific/$host/filesystem/etc/openvpn/client
+ #vpn-mk-client-cert -b $host -n hole -r iankelling.org
+ #s chown -R iank:iank .
#
- # for wireguard hole vpn
+ # example of adding to remote system 107,
+ # vpn-mk-client-cert -n hole -c 10.2.0.107 -b hd8 iankelling.org
+ #
+ # for wireguard hole vpn, use function:
# wghole
# requested from linode via a support ticket.
# esac
-### begin home vpn server setup
-
-
-# # this section done initially to make persistent keys.
-# # Also note, I temporarily set /etc/hosts so my host was
-# # b8.nz when running this, since the vpn client config
-# # generator assumes we need to go to that server to get
-# # server keys.
-# vpn-server-setup -rds
-# s cp -r --parents /etc/openvpn/easy-rsa/keys /p/c/filesystem
-# s chown -R 1000:1000 /p/c/filesystem/etc/openvpn/easy-rsa/keys
-# # kw = kgpe work machine.
-# for host in x2 x3 kw; do
-# vpn-mk-client-cert -b $host -n home b8.nz 1196
-# dir=/p/c/machine_specific/$host/filesystem/etc/openvpn/client
-# mkdir -p $dir
-# s bash -c "cp /etc/openvpn/client/home* $dir"
-# # note: /etc/update-resolv-conf-home also exists for all systems with /p
-# done
-
-# key already exists, so this won't generate one, just the configs.
-# m vpn-server-setup -rds
-# sudo tee -a /etc/openvpn/server/server.conf <<'EOF'
-# push "dhcp-option DNS 10.0.0.1"
-# push "route 10.0.0.0 255.255.0.0"
-# client-connect /a/bin/distro-setup/vpn-client-connect
-# EOF
-# sudo sed -i --follow-symlinks 's/10.8./10.9./g;s/^\s*port\s.*/port 1196/' /etc/openvpn/server/server.conf
-
-# if [[ $HOSTNAME == tp ]]; then
-# if [[ -e /lib/systemd/system/openvpn-server@.service ]]; then
-# vpn_service=openvpn-server@server
-# else
-# vpn_service=openvpn@server
-# fi
-# sgo $vpn_service
-# fi
-### end vpn server setup
-
##### rss2email
if mountpoint /p &>/dev/null; then
# note, see bashrc for more documentation.
sgo openvpn-client@hole
fi
-if [[ $HOSTNAME == frodo ]]; then
- vpn-mk-client-cert -b frodo -n hole iankelling.org
-fi
-
############# begin syncthing setup ###########
case $HOSTNAME in
kd|frodo)
;;
esac
-mkdir -p $tdir
+sudo mkdir -p $tdir
# adapted from /var/lib/dpkg/info/transmission-daemon.postinst
# 450 seems likely to be unused. we need to specify one or else
f=$tdir/transmission-daemon
for d in $tdir/partial-torrents $tdir/torrents $f; do
if [[ ! -d $d ]]; then
- mkdir $d
+ sudo mkdir -p $d
fi
sudo chown -R debian-transmission:user2 $d
done
rm -rf /home/iank/.mpv
-if [[ $HOSTNAME != frodo ]]; then
- # remove. i moved this into dns
- echo | s cedit hole /etc/hosts ||:
-fi
-
if [[ ! -e ~/.local/bin/pip ]]; then
tmp=$(mktemp)
wget -O$tmp https://bootstrap.pypa.io/get-pip.py
pi desktop-file-utils
m /a/bin/distro-setup/mymimes
-
-# stop autopoping windows when i plug in an android phone.
-# dbus-launch makes this work within an ssh connection, otherwise you get this message,
-# with still 0 exit code.
-# dconf-WARNING **: failed to commit changes to dconf: Cannot autolaunch D-Bus without X11 $DISPLAY
-m dbus-launch gsettings set org.gnome.desktop.media-handling automount-open false
-
+if type -p dbus-launch >/dev/null; then
+ # stop autopoping windows when i plug in an android phone.
+ # dbus-launch makes this work within an ssh connection, otherwise you get this message,
+ # with still 0 exit code.
+ # dconf-WARNING **: failed to commit changes to dconf: Cannot autolaunch D-Bus without X11 $DISPLAY
+ m dbus-launch gsettings set org.gnome.desktop.media-handling automount-open false
+fi
# on grub upgrade, we get prompts unless we do this
devs=()
esac
case $HOSTNAME in
- # frodo needs upgrade first.
- frodo) : ;;
# todo, for limiting node exporter http,
# either use iptables or, in
# /etc/default/prometheus-node-exporter
pi tor
m /a/bin/buildscripts/tor-browser
# one root command needed to install
-s ln -sf /a/opt/tor-browser_en-US/Browser/start-tor-browser /usr/local/bin
+s ln -sf /a/opt/tor-browser/Browser/start-tor-browser /usr/local/bin
# nfs server
#!/bin/bash
-source ~/.bashrc
+
+f=/usr/local/lib/err;test -r $f || { echo "error: $0 no $f" >&2;exit 1;}; . $f
+[[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@"
+
+this_file="$(readlink -f -- "${BASH_SOURCE[0]}")"
+readonly this_file
+this_dir="${this_file%/*}"
+readonly this_dir
+cd "$this_dir"
+
+usage() {
+ cat <<EOF
+Usage: ${0##*/} [-f]
+Update ip in remote nameserver.
+
+-f Force update even if ip hasn't changed.
+-h|--help Print help and exit.
+
+Note: Uses util-linux getopt option parsing: spaces between args and
+options, short options can be combined, options before args.
+EOF
+ exit $1
+}
+
+##### begin command line parsing ########
+
+# ensure we can handle args with spaces or empty.
+ret=0; getopt -T || ret=$?
+[[ $ret == 4 ]] || { echo "Install util-linux for enhanced getopt" >&2; exit 1; }
+
+force=false # default
+temp=$(getopt -l help hf "$@") || usage 1
+eval set -- "$temp"
+while true; do
+ case $1 in
+ -f) force=true ;;
+ --) shift; break ;;
+ *) echo "$0: unexpected args: $*" >&2 ; usage 1 ;;
+ esac
+ shift
+done
+
+##### end command line parsing ########
main() {
case $gateway in
10.2.0.1)
+ dyndomain=b8.nz
dynhost=i.b8.nz
;;
*)
;;
esac
+ # We check if we are at home by testing gateway ssh
+ # fingerprint. However, if we found in the past that we are, I dont
+ # like to spam its logs with ssh login attempts, so just check if our
+ # gateway interface has an increasing amount of packets sent +
+ # received from last time.
athome=false
if [[ -s /dev/shm/dynamic-ip-update-state ]]; then
oldbytes=$(cat /dev/shm/dynamic-ip-update-state)
return 0
fi
if ip4=$(curl -s4 https://iankelling.org/cgi/pubip); then
- if [[ $cur4 && $ip4 && $cur4 != $ip4 ]]; then
+ if $force || [[ $cur4 && $ip4 && $cur4 != $ip4 ]]; then
up4=true # update ipv4
fi
fi
fi
fi
- if [[ $cur6 != $ip6 ]]; then
+ if $force || [[ $cur6 != $ip6 ]]; then
up6=true
fi
cat >>$f <<EOF
update delete $dynhost. A
update add $dynhost. 300 A $ip4
+update delete $dyndomain. A
+update add $dyndomain. 300 A $ip4
EOF
fi
quit
EOF
- nsupdate -k /p/c/machine_specific/vps/filesystem/etc/bind/Kb8.nz.*.private <$f
+ nsupdate -k /p/c/machine_specific/vps/filesystem/etc/bind/Kb8.nz.*.private <$f || nsupdate_fails=$((nsupdate_fails + 1))
sed -i 's/^server .*/server bk.b8.nz/' $f
- nsupdate -k /p/c/machine_specific/vps/filesystem/etc/bind/Kb8.nz.*.private <$f
-
-
+ nsupdate -k /p/c/machine_specific/vps/filesystem/etc/bind/Kb8.nz.*.private <$f || nsupdate_fails=$((nsupdate_fails + 1))
+ if (( nsupdate_fails > nsupdate_fail_limit )); then
+ echo error: nsupdate is persistently failing >&2
+ exit 1
+ fi
}
loop-main() {
done
}
-
+nsupdate_fails=0
if [[ $INVOCATION_ID ]]; then
+ nsupdate_fail_limit=10
loop-main
else
+ nsupdate_fail_limit=0
main
fi
--- /dev/null
+[Unit]
+Description=Navidrome
+After=remote-fs.target network.target
+AssertPathExists=/i/navidrome
+
+[Install]
+WantedBy=multi-user.target
+
+[Service]
+User=iank
+Group=iank
+Type=simple
+ExecStart=/i/navidrome/navidrome --configfile "/i/navidrome/navidrome.toml"
+WorkingDirectory=/i/navidrome
+TimeoutStopSec=20
+KillMode=process
+Restart=on-failure
+
+# See https://www.freedesktop.org/software/systemd/man/systemd.exec.html
+DevicePolicy=closed
+NoNewPrivileges=yes
+PrivateTmp=yes
+PrivateUsers=yes
+ProtectControlGroups=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
+SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap
+ReadWritePaths=/i/navidrome
+
+# You can uncomment the following line if you're not using the jukebox This
+# will prevent navidrome from accessing any real (physical) devices
+#PrivateDevices=yes
+
+# You can change the following line to `strict` instead of `full` if you don't
+# want navidrome to be able to write anything on your filesystem outside of
+# /var/lib/navidrome.
+ProtectSystem=full
+
+# You can uncomment the following line if you don't have any media in /home/*.
+# This will prevent navidrome from ever reading/writing anything there.
+#ProtectHome=true
+
+# You can customize some Navidrome config options by setting environment variables here. Ex:
+#Environment=ND_BASEURL="/navidrome"
--- /dev/null
+#!/bin/sh
+
+wall -n off: $$ shutdown in 30 seconds
+sleep 30
+systemctl poweroff
-#!/bin/sh
+#!/bin/bash -r
-systemctl suspend
+for (( i=0; i<3; i++ )); do
+ systemctl suspend
+ wall -n spend: $$ suspending in 30 seconds
+ sleep 30
+done
+wall -n spend: $$ shutdown in 30 seconds
+shutdown
--- /dev/null
+#!/bin/bash -r
+
+# unsuspend
+pkill -f /usr/local/bin/spend
set $mod Mod4
bindsym $mod+2 exec "pavucontrol"
-# ian: dunno why but i needed this version at some point, then
-# it mysteriously stopped working, but works on the cli. 2022-12
-#bindsym $mod+3 exec "abroswer"
-bindsym $mod+3 exec "abrowser -no-remote -P sfw"
+# calling without -no-remote makes this to be the instance that links
+# will open in from other applications.
+bindsym $mod+3 exec "abrowser"
+# calling just abrowser mysteriously stopped working,
+# so I figured out this is how to get output, but then
+# it suddenly started working again.
+#bindsym $mod+3 exec "abrowser 2>&1 >/tmp/l"
+#bindsym $mod+3 exec "abrowser -no-remote -P sfw"
bindsym $mod+4 exec "abrowser -no-remote -P firefox-main-profile"
bindsym $mod+5 exec "/usr/local/bin/start-tor-browser"
bindsym $mod+6 exec "/a/bin/redshift.sh"
# Copyright (C) 2019 Ian Kelling
# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# setup automatic decryption on boot using host-specific key file.
+# When changing a hostname, that key needs updating.
+
set -eE -o pipefail
trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?"' ERR
if [[ $INVOCATION_ID ]]; then
if [[ -e /b/bash_unpublished/source-state ]]; then
+ # this is the canonical one
source /b/bash_unpublished/source-state
+ elif [[ -e /dev/shm/iank-status ]]; then
+ # This one gets copied by system-status and is useful because it
+ # exists when /a is unmounted.
+ source /dev/shm/iank-status
fi
if [[ $MAIL_HOST && $MAIL_HOST != $HOSTNAME ]]; then
echo "$0: exiting early: running under systemd as MAIL_HOST"
do_o=false
fi
-if [[ $HOSTNAME == frodo ]]; then
- fstab <<EOF
-$crypt_dev /i btrfs noatime,subvol=i$mopts 0 0
-EOF
-fi
-
-
##### end setup fstab for subvols we care about ######
m btrfs property set -ts $leaf ro true
### begin check if leaf is different, delete it if not ###
- if [[ -e /a/opt/btrfs-snapshots-diff/btrfs-snapshots-diff.py ]]; then
- source /a/bin/distro-functions/src/package-manager-abstractions
- #pi python-jmespath # dependency of btrfs-snapshots-diff
- # todo: need python3 port of btrfs-snapshots-diff, py2 no exist on nabia
- parentid=$(btrfs sub show $leaf | awk '$1 == "Parent" && $2 == "UUID:" {print $3}')
- bsubs=(btrbk/$vol.*)
- bsub=
- # go in reverse order as its more likely to be at the end
- for ((i=${#bsubs[@]}-1; i>=0; i--)); do
- if [[ $parentid == $(btrfs sub show ${bsubs[i]} | awk '$1 == "UUID:" {print $2}') ]]; then
- bsub=${bsubs[i]}
- break
- fi
- done
- if [[ $bsub ]]; then
- tmp=$(mktemp)
- # in testing, same subvol is 136 bytes. allow some overhead. 32 happens sometimes under systemd.
- # $ errno 32
- # EPIPE 32 Broken pipe
- btrfs send --no-data -p $bsub $leaf | head -c 1000 > $tmp || [[ $? == 141 || ${PIPESTATUS[0]} == 32 ]]
- if (( $(stat -c%s $tmp) < 1000)); then
- # example output for an empty diff:
- # Found a valid Btrfs stream header, version 1
- # o.leaf.2019-05-15T14:00:50-0400;snapshot: uuid=ba045ea30737dd449003f1ee40ec12d0, ctrasid=109533, clone_uuid=3c7e3544e486834aa71d89e5b8f30056, clone_ctransid=109533
- lines=$(/a/opt/btrfs-snapshots-diff/btrfs-snapshots-diff.py -s -f $tmp | \
- grep -vxF "Found a valid Btrfs stream header, version 1" | \
- grep -cv "^[^;]*;snapshot: ") ||:
- if [[ $lines == 0 ]]; then
- # rotate in case we find a bug, weve got 2 old ones
- tmpleaf=($vol.tmpleaf2.*)
- if (( ${#tmpleaf[@]} )); then
- x btrfs sub del ${tmpleaf[@]}
- fi
- tmpleaf=($vol.tmpleaf1.*)
- if (( ${#tmpleaf[@]} )); then
- x mv ${tmpleaf[0]} $vol.tmpleaf2.${tmpleaf[0]#$vol.tmpleaf1.}
- fi
- echo suspected identical: $bsub $leaf
- x mv $leaf $vol.tmpleaf1.${leaf#$vol.leaf.}
- fi
- fi
+ parentid=$(btrfs sub show $leaf | awk '$1 == "Parent" && $2 == "UUID:" {print $3}')
+ bsubs=(btrbk/$vol.*)
+ bsub= # base subvolume
+ # go in reverse order as its more likely to be at the end
+ for ((i=${#bsubs[@]}-1; i>=0; i--)); do
+ if [[ $parentid == $(btrfs sub show ${bsubs[i]} | awk '$1 == "UUID:" {print $2}') ]]; then
+ bsub=${bsubs[i]}
+ break
+ fi
+ done
+ if [[ $bsub ]]; then
+ tmp=$(mktemp)
+ # in testing, same subvol is 136 bytes. allow some overhead. 32 happens sometimes under systemd.
+ # $ errno 32
+ # EPIPE 32 Broken pipe
+ lines=$(btrfs send --no-data -p $bsub $leaf | btrfs receive --dump | head -n 100 | wc -l || [[ $? == 141 || ${PIPESTATUS[0]} == 32 ]])
+ if [[ $lines == 0 ]]; then
+ # example output of no differences:
+ # snapshot ./qrtest uuid=c41ff6b7-0527-f34d-95ac-190eecf54ff5 transid=2239 parent_uuid=64949e1b-4a3e-3945-9a8e-cd7b7c15d7d6 parent_transid=2239
+ echo suspected identical: $bsub $leaf
+ x btrfs sub del $leaf
fi
fi
### end check if leaf is different, delete it if not ###
+++ /dev/null
-#!/bin/bash
-# Copyright (C) 2019 Ian Kelling
-# SPDX-License-Identifier: AGPL-3.0-or-later
-if [[ -s ~/.bashrc ]];then . ~/.bashrc;fi
-# cd /k/music
-# find -type f -name '*.flac' | while read -r f; do
-# mkdir -p "../flacs/$(dirname "$f")"
-# mv -T "$f" ../flacs/"$f"
-# done
-
-
-
-# todo, add settings from /etc/default/nfs-{common,kernel-server}
-# todo: do mysql setup. kodi install. mysql backup.
-
-# in kodi, music, add files, named source, add network share,
-# server address: iank.life
-# path: k/music
-
-
-
-rm -f /a/tmp/y.sql
-
-cd /k/music
-find -type f \( -name '*.flac' -or -name '*.mp3' -or -name '*.m4a' \) | while read -r f; do
- rating=$(kid3-cli -c "get RATING" "$f")
- if [[ ! $rating ]]; then
- echo $f
- continue
- fi
- rating=$((rating*2))
-
- ## begin sql escaping
- f="${f//\"/\\\"}"
- f="${f//\'/\\\'}"
- f="${f//_/\\_}"
- f="${f//%/\\%}"
- ## end sql escaping
- d=${f%/*}
- d=${d#./}/ # use exact dir format that is in database
- cat >>/a/tmp/y.sql <<EOF
-update song
-inner join path on song.idPath = path.idPath
-set song.userrating = $rating
-where song.strFileName = '${f##*/}' and path.strPath = 'nfs://iank.life/k/music/$d';
-EOF
-done
--- /dev/null
+#!/bin/bash
+
+f=/usr/local/lib/err;test -r $f || { echo "error: $0 no $f" >&2;exit 1;}; . $f
+
+
+
+# begin star rating import from navidrome to beets:
+declare -A navirating
+for r in 1 2 3 4 5; do
+ while read -r path; do
+ beetpath="/i/m${path#/i/converted}"
+ navirating[$beetpath]=$r
+ done < <(sqlite3 /i/navidrome/navidrome.db "select path from annotation inner join media_file on item_id = id where rating = $r;")
+done
+declare -A beetrating
+for r in 1 2 3 4 5; do
+ while read -r path; do
+ beetrating[$path]=$r
+ done < <(beet ls -f '$path' rating:$r)
+done
+
+for path in "${!navirating[@]}"; do
+ r="${navirating[$path]}"
+ 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
+done
+# end star rating import from navidrome to beets:
+
+
+beet subsonicplaylist
+
+while read -r id plists; do
+ plists="${plists#;}"
+ e beet modify -y "id:$id" ${plists//;/=t }
+done < <(beet ls -f '$id $subsonic_playlist' subsonic_playlist::.)
dillo
dirmngr
dos2unix
+ dosfstools
dnsutils
python3-dnspython
duplicity
metastore
mhonarc
mmdebstrap
+ mp3gain
mps-youtube
mpv
mumble
opendkim-tools
p7zip-full
paprefs
+ parted
parted-doc
pass
pavucontrol
transmission-remote-gtk
trash-cli
tty-clock
+ uuid-runtime
vlc
wamerican-huge
wireless-tools
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<smartplaylist type="songs">
- <name></name>
- <match>all</match>
- <rule field="genre" operator="doesnotcontain">
- <value>Avant-Garde</value>
- <value>Noise</value>
- <value>Skit</value>
- <value>Spoken Word</value>
- </rule>
- <rule field="userrating" operator="greaterthan">
- <value>9</value>
- </rule>
-</smartplaylist>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<smartplaylist type="songs">
- <name>8</name>
- <match>all</match>
- <rule field="userrating" operator="greaterthan">
- <value>7</value>
- </rule>
- <rule field="genre" operator="isnot">
- <value>Avant-Garde</value>
- <value>Noise</value>
- <value>Skit</value>
- <value>Spoken Word</value>
- </rule>
-</smartplaylist>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<smartplaylist type="songs">
- <name>sad</name>
- <match>all</match>
- <rule field="genre" operator="is">
- <value>sad</value>
- </rule>
- <rule field="userrating" operator="greaterthan">
- <value>7</value>
- </rule>
-</smartplaylist>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<smartplaylist type="songs">
- <name>sad5</name>
- <match>all</match>
- <rule field="genre" operator="contains">
- <value>sad</value>
- </rule>
- <rule field="userrating" operator="greaterthan">
- <value>9</value>
- </rule>
-</smartplaylist>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<smartplaylist type="songs">
- <name>sy most</name>
- <match>all</match>
- <rule field="artist" operator="contains">
- <value>Sonic Youth</value>
- </rule>
- <rule field="userrating" operator="greaterthan">
- <value>5</value>
- </rule>
-</smartplaylist>
source /usr/local/lib/err
-pre="${0##*/}:"
+script_name="${BASH_SOURCE[0]}"
+script_name="${script_name##*/}"
+pre="${SSH_CLIENT:+$HOSTNAME} $script_name:"
m() { printf "$pre %s\n" "$*"; "$@"; }
e() { printf "$pre %s\n" "$*"; }
err() { echo "[$(date +'%Y-%m-%d %H:%M:%S%z')]: $0: $*" >&2; }
+++ /dev/null
-library: /a/bin/data/musiclibrary.blb
-directory: /i/music
-import:
- log: /a/dt/beetlog.log
- move: yes
- quiet_fallback: skip
-
-# be a little more liberal for strong matches
-match:
- strong_rec_thresh: 0.07
-plugins: discogs duplicates web info
-# all the usefull plugins I've used:
-#plugins: copyartifacts chroma discogs duplicates web
-
-#duplicates:
-# duplicates can be found based on tagged keys or a checksum program.
-# the sugggested checksum program takes hours :(
-# the default is tags, but it is broken.
-# the default ffmpeg command, using the libav equivalent based on debian recommendations
- #checksum: avconv -i {file} -f crc -
[s]
shuffle
+
+# audio
+[a]
+player-operation-mode=cplayer
fi
}
-pre="$script_name:"
+pre="${SSH_CLIENT:+$HOSTNAME} $script_name:"
m() { printf "$pre %s\n" "$*"; "$@"; }
e() { printf "$pre %s\n" "$*"; }
err() { echo "$pre ERROR: $*" >&2; }
exit $ret
fi
+# new system is usable at this point
+printf "$(tput setaf 5 2>/dev/null ||:)█$(tput sgr0 2>/dev/null||:)%.0s" $(eval echo "{1..${COLUMNS:-60}}")
+echo
+
# once I accidentally accepted incoming mail on old host. I used this script to copy over that mail:
#
# die=false; for d in o.leaf.2021-05-29T10:02:08-0400/m/{4e,md,4e2}/{,l/}!(*myarchive)/new; do if $die; then break; fi; find $d -type f -mtime -5 | while read -r f; do dir="${f%new/*}"; dir="btrbk/o.20210530T000011-0400/${dir#*/}"; fname="${f##*/}"; [[ -e $dir/new/$fname || -e $dir/cur/$fname ]] && continue; if ! e cp -a $f /${dir#*/*/}new; then echo failed cp; die=true; break; fi ; done; done
-A OUTPUT -p tcp -m tcp --sport 9091 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 9091 -j ACCEPT
-# 1195 is used for the secondary vpn server
-# 1198 is another vpn port, simpler syntax just to use range
--A OUTPUT -p udp -m udp --dport 1194:1198 -j ACCEPT
--A INPUT -p udp -m udp --sport 1194:1198 -j ACCEPT
+# 1300 is used by mullvad
+-A OUTPUT -p udp -m udp --dport 1300 -j ACCEPT
+-A INPUT -p udp -m udp --sport 1300 -j ACCEPT
-A OUTPUT -o tun0 -j ACCEPT
-A INPUT -i tun0 -j ACCEPT