this string has no description
0
bandcamp-fetch
119 lines 3.9 kB view raw
1#!/bin/sh 2set -eu 3 4MUSIC_DIR="/tank/Music" 5UA="bandcamp-import/0.1 (https://j3s.sh)" 6 7die() { printf '%s\n' "$*" >&2; exit 1; } 8 9[ "${1:-}" ] || die "usage: bandcamp-fetch <download-url>" 10 11url="$1" 12tmp=$(mktemp -d) 13trap 'rm -rf "$tmp"' EXIT 14 15# download 16printf 'downloading...\n' 17wget -q -O "$tmp/download.zip" "$url" || die "download failed" 18unzip -q -o "$tmp/download.zip" -d "$tmp/album" || die "not a valid zip" 19rm "$tmp/download.zip" 20 21# find the album dir (bandcamp zips sometimes have a subdirectory) 22inner=$(find "$tmp/album" -mindepth 1 -maxdepth 1 -type d -print -quit) 23if [ -d "$inner" ]; then 24 albumdir="$inner" 25else 26 albumdir="$tmp/album" 27fi 28 29# read artist/album from existing tags in the first audio file 30probe=$(find "$albumdir" \( -name '*.flac' -o -name '*.mp3' \) | sort | head -1) 31[ -f "$probe" ] || die "no audio files found" 32 33artist=$(ffprobe -v quiet -show_entries format_tags=artist -of default=nw=1:nk=1 "$probe") 34album=$(ffprobe -v quiet -show_entries format_tags=album -of default=nw=1:nk=1 "$probe") 35[ "$artist" ] && [ "$album" ] || die "can't read artist/album tags from: $probe" 36 37printf 'parsed: %s / %s\n' "$artist" "$album" 38 39# query musicbrainz for the release 40encoded_artist=$(printf '%s' "$artist" | jq -sRr @uri) 41encoded_album=$(printf '%s' "$album" | jq -sRr @uri) 42 43mb_url="https://musicbrainz.org/ws/2/release/?query=artist:${encoded_artist}+release:${encoded_album}&fmt=json&limit=1" 44mb_json=$(curl -s -H "User-Agent: $UA" "$mb_url") 45 46score=$(printf '%s' "$mb_json" | jq -r '.releases[0].score // 0') 47if [ "$score" -lt 80 ] 2>/dev/null; then 48 printf 'musicbrainz: best match score %s (< 80), skipping metadata\n' "$score" 49 mb_found=0 50else 51 mb_found=1 52 mb_id=$(printf '%s' "$mb_json" | jq -r '.releases[0].id') 53 mb_artist=$(printf '%s' "$mb_json" | jq -r '.releases[0]["artist-credit"][0].name') 54 mb_album=$(printf '%s' "$mb_json" | jq -r '.releases[0].title') 55 mb_date=$(printf '%s' "$mb_json" | jq -r '.releases[0].date // empty') 56 mb_year=$(printf '%s' "$mb_date" | cut -c1-4) 57 58 printf 'musicbrainz: %s / %s (%s) [%s]\n' "$mb_artist" "$mb_album" "$mb_year" "$mb_id" 59 60 # fetch track listing 61 sleep 1 # rate limit 62 release_json=$(curl -s -H "User-Agent: $UA" \ 63 "https://musicbrainz.org/ws/2/release/${mb_id}?inc=recordings&fmt=json") 64 65 # use mb values 66 artist="$mb_artist" 67 album="$mb_album" 68fi 69 70# tag files 71tag_flac() { 72 file="$1" tracknum="$2" title="$3" 73 metaflac --remove-all-tags "$file" 74 metaflac --set-tag="ARTIST=$artist" "$file" 75 metaflac --set-tag="ALBUM=$album" "$file" 76 metaflac --set-tag="TRACKNUMBER=$tracknum" "$file" 77 metaflac --set-tag="TITLE=$title" "$file" 78 [ "${mb_year:-}" ] && metaflac --set-tag="DATE=$mb_year" "$file" 79 [ "${mb_id:-}" ] && metaflac --set-tag="MUSICBRAINZ_ALBUMID=$mb_id" "$file" 80} 81 82tag_mp3() { 83 file="$1" tracknum="$2" title="$3" 84 mid3v2 -a "$artist" -A "$album" -t "$title" -T "$tracknum" \ 85 ${mb_year:+-y "$mb_year"} "$file" 86} 87 88# collect audio files sorted by name (bandcamp names them 01, 02, ...) 89tracknum=0 90find "$albumdir" \( -name '*.flac' -o -name '*.mp3' \) | sort | while read -r f; do 91 tracknum=$((tracknum + 1)) 92 93 if [ "$mb_found" = 1 ]; then 94 # get title from musicbrainz track listing 95 mb_title=$(printf '%s' "$release_json" | \ 96 jq -r --argjson n "$((tracknum - 1))" \ 97 '.media[0].tracks[$n].title // empty') 98 fi 99 100 # fall back to filename 101 if [ -z "${mb_title:-}" ]; then 102 mb_title=$(basename "$f" | sed 's/^[0-9]*[. -]*//' | sed 's/\.[^.]*$//') 103 fi 104 105 ext="${f##*.}" 106 printf ' %02d %s\n' "$tracknum" "$mb_title" 107 case "$ext" in 108 flac) tag_flac "$f" "$tracknum" "$mb_title" ;; 109 mp3) tag_mp3 "$f" "$tracknum" "$mb_title" ;; 110 esac 111done 112 113# move to music library 114dest="$MUSIC_DIR/$artist/$album" 115mkdir -p "$dest" 116find "$albumdir" \( -name '*.flac' -o -name '*.mp3' -o -name '*.jpg' -o -name '*.png' \) \ 117 -exec mv {} "$dest/" \; 118 119printf 'done: %s\n' "$dest"