Files
fuzic/src/sh/local.sh

239 lines
8.3 KiB
Bash

gettags() {
ffprobe -v error -show_entries format_tags -print_format json "$1" |
$JQ -r --compact-output '.format.tags | {
trackid: (."MusicBrainz Release Track Id" // ."MUSICBRAINZ_RELEASETRACKID" // ."MusicBrainz/Release Track Id" // ""),
releaseid: (."MusicBrainz Album Id" // ."MUSICBRAINZ_ALBUMID" // ."MusicBrainz/Album Id" // "")
}'
}
# Read music files in specified directory and create json file that points to
# all relevant MusicBrainz IDs.
# @input $1: Path to directory with album
decorate() {
if [ -f "$1/$DECORATION_FILENAME" ]; then
info "Directory $1 has already been decorated (skipping)"
return 0
fi
decoration=$($JQ -n '.tracks = {}')
tmpf=$(mktemp)
(cd "$1" && find . -type f -iname '*.mp3' -o -iname '*.mp4' -o -iname '*.flac' -o -iname '*.m4a') >"$tmpf"
while IFS= read -r f; do
mbid=$(gettags "$1/$f")
rid=$(echo "$mbid" | $JQ -r '.releaseid')
tid=$(echo "$mbid" | $JQ -r '.trackid')
if [ ! "$rid" ] || [ ! "$tid" ]; then
err "File $f: Seems not tagged"
releaseid=""
break
fi
if [ "${releaseid:-}" ]; then
if [ "$releaseid" != "$rid" ]; then
err "Directory $1 contains files of different releases"
releaseid=""
break
fi
else
info "Decorating $1 as release $rid"
releaseid="$rid"
fi
decoration=$(echo "$decoration" | $JQ ".tracks += {\"$tid\": \"$f\"}")
done <"$tmpf"
rm -f "$tmpf"
if [ "$releaseid" ]; then
echo "$decoration" | $JQ --compact-output ".releaseid = \"$releaseid\"" >"$1/$DECORATION_FILENAME"
else
return 1
fi
}
# Load missing cache entries (batch mode)
# argument $1: type
# argument $2: File with one ID per line
__batch_load_missing() {
tmpf=$(mktemp)
while IFS= read -r mbid; do
if ! in_cache "$1" "$mbid"; then
echo "$mbid" >>"$tmpf"
fi
done <"$2"
if [ -s "$tmpf" ]; then
lines=$(wc -l "$tmpf" | cut -d ' ' -f 1)
if [ "$lines" -gt 0 ]; then
case "$1" in
"$TYPE_ARTIST")
tt="artists"
;;
"$TYPE_RELEASEGROUP")
tt="release groups"
;;
"$TYPE_RELEASE")
tt="releases"
;;
esac
info "Fetching $lines missing $tt"
cnt=0
while IFS= read -r mbid; do
case "$1" in
"$TYPE_ARTIST")
name=$(mb_artist "$mbid" | $JQ -r ".name")
;;
"$TYPE_RELEASEGROUP")
name=$(mb_releasegroup "$mbid" | $JQ -r ".title")
;;
"$TYPE_RELEASE")
name=$(mb_release "$mbid" | $JQ -r ".title")
;;
esac
cnt=$((cnt + 1))
printf "\033[2K\r%d/%d (%s: %s)" "$cnt" "$lines" "$mbid" "$name"
sleep 1
done <"$tmpf"
printf "\n"
fi
fi
rm -f "$tmpf"
}
LOCALDATADIR="$HOME/.cache/$APP_NAME/local"
LOCALDATA_ARTISTS="$LOCALDATADIR/artists"
LOCALDATA_RELEASEGROUPS="$LOCALDATADIR/releasegroups"
LOCALDATA_RELEASES="$LOCALDATADIR/releases"
LOCALDATA_ARTISTS_VIEW="$LOCALDATADIR/artists_view"
LOCALDATA_RELEASEGROUPS_VIEW="$LOCALDATADIR/releasegroups_view"
LOCALDATA_RELEASES_VIEW="$LOCALDATADIR/releases_view"
DECORATION_FILENAME=${DECORATION_FILENAME:-"mbid.json"}
# Precompute views
precompute_view() {
info "Precomputing artist view"
while IFS= read -r aid; do
mb_artist "$aid" | $JQ -r '[
.id,
.type,
.name,
.disambiguation,
.["life-span"].begin,
.["life-span"].end
] | join("\t")'
done <"$LOCALDATA_ARTISTS" |
awk \
-F "\t" \
-v file_local_artists="${LOCALDATA_ARTISTS:-}" \
-v format_person="$AV_PERSON" \
-v format_group="$AV_GROUP" \
-v format_disambiguation="$AV_DISAMBIGUATION" \
-v format_local="$FORMAT_LOCAL" \
"$AWK_ARTISTS" |
sort |
column -t -s "$(printf '\t')" -E 0 |
sed 's| \+\([0-9a-f-]\+\) \+\([0-9a-f-]\+\)$|\t\1\t\2|' >"$LOCALDATA_ARTISTS_VIEW"
info "Precomputing releasegroup view"
while IFS= read -r rgid; do
mb_releasegroup "$rgid" | $JQ -r '[
.id,
."primary-type",
(."secondary-types" // []|join(";")),
."first-release-date",
.title,
(."artist-credit" | map(([.name, .joinphrase]|join(""))) | join(""))
] | join("\t")'
done <"$LOCALDATA_RELEASEGROUPS" |
awk \
-F "\t" \
-v file_local_releasegroups="${LOCALDATA_RELEASEGROUPS:-}" \
-v format_release="$RGV_RELEASE" \
-v format_release_w_artist="$RGV_RELEASE_W_ARTIST" \
-v format_year="$RGV_YEAR" \
-v format_album="$FORMAT_TYPE_ALBUM" \
-v format_single="$FORMAT_TYPE_SINGLE" \
-v format_ep="$FORMAT_TYPE_EP" \
-v format_broadcast="$FORMAT_TYPE_BROADCAST" \
-v format_other="$FORMAT_TYPE_OTHER" \
-v format_has_secondary="$FORMAT_TYPE_HAS_SECONDARY" \
-v format_secondary="$FORMAT_TYPE_SECONDARY" \
-v format_compilation="$FORMAT_TYPE_SECONDARY_COMPILATION" \
-v format_soundtrack="$FORMAT_TYPE_SECONDARY_SOUNDTRACK" \
-v format_spokenword="$FORMAT_TYPE_SECONDARY_SPOKENWORD" \
-v format_interview="$FORMAT_TYPE_SECONDARY_INTERVIEW" \
-v format_audiobook="$FORMAT_TYPE_SECONDARY_AUDIOBOOK" \
-v format_audiodrama="$FORMAT_TYPE_SECONDARY_AUDIODRAMA" \
-v format_live="$FORMAT_TYPE_SECONDARY_LIVE" \
-v format_remix="$FORMAT_TYPE_SECONDARY_REMIX" \
-v format_djmix="$FORMAT_TYPE_SECONDARY_DJMIX" \
-v format_mixtape="$FORMAT_TYPE_SECONDARY_MIXTAPE" \
-v format_demo="$FORMAT_TYPE_SECONDARY_DEMO" \
-v format_fieldrec="$FORMAT_TYPE_SECONDARY_FIELDREC" \
-v format_local="$FORMAT_LOCAL" \
"$AWK_RELEASEGROUPS" |
sort -n -r |
cut -d "$(printf '\t')" -f 2- |
column -t -s "$(printf '\t')" -E 0 |
sed 's| \+\([0-9a-f-]\+\) \+\([0-9a-f-]\+\)$|\t\1\t\2|' >"$LOCALDATA_RELEASEGROUPS_VIEW"
info "Precomputing release view"
cut -d "$(printf '\t')" -f 1 "$LOCALDATA_RELEASES" |
while IFS= read -r rid; do
mb_release "$rid" | $JQ -r '[
.id,
.status,
.date,
."cover-art-archive".count,
(."label-info" | map(.label.name) | unique | join(", ")),
(.media | map(."track-count") | add),
(.media | map(.format) | unique | join(", ")),
.country,
.title,
(."artist-credit" | map(([.name, .joinphrase]|join(""))) | join(""))
] | join("\t")'
done |
awk \
-F "\t" \
-v file_local_releases="${LOCALDATA_RELEASES:-}" \
-v release_official="$FORMAT_STATUS_OFFICIAL" \
-v release_promotion="$FORMAT_STATUS_PROMO" \
-v release_bootleg="$FORMAT_STATUS_BOOTLEG" \
-v release_pseudo="$FORMAT_STATUS_PSEUDO" \
-v release_withdrawn="$FORMAT_STATUS_WITHDRAWN" \
-v release_expunged="$FORMAT_STATUS_EXPUNGED" \
-v release_cancelled="$FORMAT_STATUS_CANCELLED" \
-v release_format="$RV_FORMAT" \
-v release_format_title_artist="$RV_TITLE_ARTIST" \
-v release_format_title="$RV_TITLE" \
-v release_format_artist="$RV_ARTIST" \
-v format_local="$FORMAT_LOCAL" \
"$AWK_RELEASES" |
sort -n -r |
cut -d "$(printf '\t')" -f 2- |
column -t -s "$(printf '\t')" -E 0 |
sed 's| \+\([0-9a-f-]\+\) \+\([0-9a-f-]\+\):\(.*$\)$|\t\1\t\2\t\3|' >"$LOCALDATA_RELEASES_VIEW"
}
# Load local music
# argument $1: path to decorated music files
load_local() {
[ -d "$LOCALDATADIR" ] || mkdir -p "$LOCALDATADIR"
tmpreleases=$(mktemp)
[ -f "$tmpreleases" ] || exit 1
info "Locating and parsing decoration files ($DECORATION_FILENAME)"
find "$1" -type f -name "$DECORATION_FILENAME" -print0 |
xargs -0 -P 4 $JQ -r '.releaseid+"\t"+input_filename' |
tee "$LOCALDATA_RELEASES" |
cut -d "$(printf '\t')" -f 1 >"$tmpreleases"
__batch_load_missing "$TYPE_RELEASE" "$tmpreleases"
# Get release groups and album artists
while IFS= read -r rid; do
mb=$(mb_release "$rid")
echo "$mb" | $JQ -r '."release-group".id' >>"$LOCALDATA_RELEASEGROUPS"
echo "$mb" | $JQ -r '."release-group"."artist-credit" | map(.artist.id) | join("\n")' >>"$LOCALDATA_ARTISTS"
done <"$tmpreleases"
tf=$(mktemp)
sort "$LOCALDATA_RELEASEGROUPS" | uniq >"$tf" && mv "$tf" "$LOCALDATA_RELEASEGROUPS"
sort "$LOCALDATA_ARTISTS" | uniq >"$tf" && mv "$tf" "$LOCALDATA_ARTISTS"
# Populate cache with missing data
__batch_load_missing "$TYPE_RELEASEGROUP" "$LOCALDATA_RELEASEGROUPS"
__batch_load_missing "$TYPE_ARTIST" "$LOCALDATA_ARTISTS"
rm -f "$tmpreleases"
info "Resetting views"
rm -f "$LOCALDATA_ARTISTS_VIEW" "$LOCALDATA_RELEASEGROUPS_VIEW" "$LOCALDATA_RELEASES_VIEW"
precompute_view
}