From bb9fd9a7e0cda9893e7d8573e5f64ff695caff39 Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Fri, 20 Mar 2026 09:12:17 -0400 Subject: tagopus --- tagopus | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100755 tagopus (limited to 'tagopus') diff --git a/tagopus b/tagopus new file mode 100755 index 0000000..165cdda --- /dev/null +++ b/tagopus @@ -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 -- cgit v1.2.3