diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2026-03-20 09:12:17 -0400 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2026-03-20 09:12:17 -0400 |
| commit | bb9fd9a7e0cda9893e7d8573e5f64ff695caff39 (patch) | |
| tree | df81a5dcb91c11ce73a830d5003c7829b6e72a8a | |
| parent | 4e1f21f9d882d392c8636a1ce095f9ec145a74ab (diff) | |
| download | bin-master.zip | |
| -rwxr-xr-x | tagopus | 93 |
1 files changed, 93 insertions, 0 deletions
@@ -0,0 +1,93 @@ +#!/bin/sh + +usage() { + cat >&2 <<'EOF' +Usage: tagopus [OPTIONS] FILE... + +Options: + -a ARTIST Set artist + -b ALBUM Set album + -y DATE Set date (year) + -D Infer artist/album from directory structure (artist/album/NN title.opus) + Explicit -a/-b flags override inferred values. + +Track number and title are inferred from each filename's leading numeric prefix. +EOF + exit 1 +} + +artist= +album= +date= +infer_dirs=0 + +while getopts 'a:b:y:D' opt; do + case $opt in + a) artist=$OPTARG ;; + b) album=$OPTARG ;; + y) date=$OPTARG ;; + D) infer_dirs=1 ;; + *) usage ;; + esac +done +shift $((OPTIND - 1)) + +[ $# -eq 0 ] && usage + +# Validate fields that must come from flags and are always required +err=0 +[ -z "$date" ] && { printf 'Error: date is required (-y)\n' >&2; err=1; } + +# Without -D, artist/album must be supplied via flags +if [ $infer_dirs -eq 0 ]; then + [ -z "$artist" ] && { printf 'Error: artist is required (-a or -D)\n' >&2; err=1; } + [ -z "$album" ] && { printf 'Error: album is required (-b or -D)\n' >&2; err=1; } +fi + +[ $err -eq 1 ] && exit 1 + +numtracks=$# + +for f in "$@"; do + base=$(basename "$f") + name="${base%.opus}" + + # Extract leading numeric prefix as track number (variable width) + tracknum=$(printf '%s' "$name" | sed -n 's/^\([0-9][0-9]*\).*/\1/p') + if [ -z "$tracknum" ]; then + printf 'Error: cannot parse track number from filename: %s\n' "$f" >&2 + exit 1 + fi + + # Title is everything after the numeric prefix and any whitespace + title=$(printf '%s' "$name" | sed 's/^[0-9][0-9]*[[:space:]]*//') + if [ -z "$title" ]; then + printf 'Error: cannot parse title from filename: %s\n' "$f" >&2 + exit 1 + fi + + # Start with flag values; fill in from directory structure if -D and not already set + file_artist=$artist + file_album=$album + + if [ $infer_dirs -eq 1 ]; then + album_dir=$(pwd) + artist_dir=$(dirname "$album_dir") + [ -z "$file_album" ] && file_album=$(basename "$album_dir") + [ -z "$file_artist" ] && file_artist=$(basename "$artist_dir") + fi + + # Final check — -D may have been given but paths might not resolve usefully + err=0 + [ -z "$file_artist" ] && { printf 'Error: could not determine artist for: %s\n' "$f" >&2; err=1; } + [ -z "$file_album" ] && { printf 'Error: could not determine album for: %s\n' "$f" >&2; err=1; } + [ $err -eq 1 ] && exit 1 + + opustags -i "$f" \ + -s "artist=$file_artist" \ + -s "album=$file_album" \ + -s "date=$date" \ + -s "tracknumber=$tracknum" \ + -s "tracktotal=$numtracks" \ + -s "title=$title" +done |