this string has no description
0
bandcamp-fetch
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"