#!/bin/sh set -eu MUSIC_DIR="/tank/Music" UA="bandcamp-import/0.1 (https://j3s.sh)" die() { printf '%s\n' "$*" >&2; exit 1; } [ "${1:-}" ] || die "usage: bandcamp-fetch " url="$1" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT # download printf 'downloading...\n' wget -q -O "$tmp/download.zip" "$url" || die "download failed" unzip -q -o "$tmp/download.zip" -d "$tmp/album" || die "not a valid zip" rm "$tmp/download.zip" # find the album dir (bandcamp zips sometimes have a subdirectory) inner=$(find "$tmp/album" -mindepth 1 -maxdepth 1 -type d -print -quit) if [ -d "$inner" ]; then albumdir="$inner" else albumdir="$tmp/album" fi # read artist/album from existing tags in the first audio file probe=$(find "$albumdir" \( -name '*.flac' -o -name '*.mp3' \) | sort | head -1) [ -f "$probe" ] || die "no audio files found" artist=$(ffprobe -v quiet -show_entries format_tags=artist -of default=nw=1:nk=1 "$probe") album=$(ffprobe -v quiet -show_entries format_tags=album -of default=nw=1:nk=1 "$probe") [ "$artist" ] && [ "$album" ] || die "can't read artist/album tags from: $probe" printf 'parsed: %s / %s\n' "$artist" "$album" # query musicbrainz for the release encoded_artist=$(printf '%s' "$artist" | jq -sRr @uri) encoded_album=$(printf '%s' "$album" | jq -sRr @uri) mb_url="https://musicbrainz.org/ws/2/release/?query=artist:${encoded_artist}+release:${encoded_album}&fmt=json&limit=1" mb_json=$(curl -s -H "User-Agent: $UA" "$mb_url") score=$(printf '%s' "$mb_json" | jq -r '.releases[0].score // 0') if [ "$score" -lt 80 ] 2>/dev/null; then printf 'musicbrainz: best match score %s (< 80), skipping metadata\n' "$score" mb_found=0 else mb_found=1 mb_id=$(printf '%s' "$mb_json" | jq -r '.releases[0].id') mb_artist=$(printf '%s' "$mb_json" | jq -r '.releases[0]["artist-credit"][0].name') mb_album=$(printf '%s' "$mb_json" | jq -r '.releases[0].title') mb_date=$(printf '%s' "$mb_json" | jq -r '.releases[0].date // empty') mb_year=$(printf '%s' "$mb_date" | cut -c1-4) printf 'musicbrainz: %s / %s (%s) [%s]\n' "$mb_artist" "$mb_album" "$mb_year" "$mb_id" # fetch track listing sleep 1 # rate limit release_json=$(curl -s -H "User-Agent: $UA" \ "https://musicbrainz.org/ws/2/release/${mb_id}?inc=recordings&fmt=json") # use mb values artist="$mb_artist" album="$mb_album" fi # tag files tag_flac() { file="$1" tracknum="$2" title="$3" metaflac --remove-all-tags "$file" metaflac --set-tag="ARTIST=$artist" "$file" metaflac --set-tag="ALBUM=$album" "$file" metaflac --set-tag="TRACKNUMBER=$tracknum" "$file" metaflac --set-tag="TITLE=$title" "$file" [ "${mb_year:-}" ] && metaflac --set-tag="DATE=$mb_year" "$file" [ "${mb_id:-}" ] && metaflac --set-tag="MUSICBRAINZ_ALBUMID=$mb_id" "$file" } tag_mp3() { file="$1" tracknum="$2" title="$3" mid3v2 -a "$artist" -A "$album" -t "$title" -T "$tracknum" \ ${mb_year:+-y "$mb_year"} "$file" } # collect audio files sorted by name (bandcamp names them 01, 02, ...) tracknum=0 find "$albumdir" \( -name '*.flac' -o -name '*.mp3' \) | sort | while read -r f; do tracknum=$((tracknum + 1)) if [ "$mb_found" = 1 ]; then # get title from musicbrainz track listing mb_title=$(printf '%s' "$release_json" | \ jq -r --argjson n "$((tracknum - 1))" \ '.media[0].tracks[$n].title // empty') fi # fall back to filename if [ -z "${mb_title:-}" ]; then mb_title=$(basename "$f" | sed 's/^[0-9]*[. -]*//' | sed 's/\.[^.]*$//') fi ext="${f##*.}" printf ' %02d %s\n' "$tracknum" "$mb_title" case "$ext" in flac) tag_flac "$f" "$tracknum" "$mb_title" ;; mp3) tag_mp3 "$f" "$tracknum" "$mb_title" ;; esac done # move to music library dest="$MUSIC_DIR/$artist/$album" mkdir -p "$dest" find "$albumdir" \( -name '*.flac' -o -name '*.mp3' -o -name '*.jpg' -o -name '*.png' \) \ -exec mv {} "$dest/" \; printf 'done: %s\n' "$dest"