djcev.com

//

Git Repos / dotfiles / bin / tag_audio.sh

Last commit to this repo was on 2022-02-15 at 01:35.

#!/bin/sh
#
# Copyright (c) 2016-2022, Cameron Vanderzanden. Available under the
# terms of the 2-clause BSD license. Please see the file "LICENSE" in
# the root dir of this repository for more information.
#
#- tag_audio.sh 2016/11/04 cev
#- tag_audio.sh 2021/07/23 cev
## Usage: tag_audio.sh [-3fhpV] [-e tag] [-k tag] [-v value] <file>
##
## Edit the metadata "tags" of an audio file. Supports flac and mp3 files
## via metaflac and mid3v2, respectively. Recognizes the following tags:
## artist, tracktitle, albumtitle, date, genre, composer, performer, bpm,
## initialkey, albumartist, tracknumber, tracktotal, discnumber, disctotal,
## comment.
##
## -3 Operate on mp3 files
## -e Edit <tag> of <file>
## -f Operate on flac files
## -h Show help
## -k Edit <tag> of <file>. Synonymous with -e .
## -p List recognized tags and their values as stored in <file>.
## -v New <value> for <tag> when -e is specified. Non-interactive.
## -V Print version info
#

[ -x $HOME/bin/cev_print.sh ] && . $HOME/bin/cev_print.sh

self="${0##*'/'}"
filetype=""
fileext=""
do_tag_print=""
do_tag_edit=""
do_tag_new_value=""

# known tags / tags we care about
artist=""
tracktitle=""
albumtitle=""
year=""
genre=""
composer=""
performer=""
albumartist=""
tracknumber=""
tracktotal=""
discnumber=""
disctotal=""
comment=""
bpm=""
initialkey=""
rgrl=""
rgag=""
rgap=""
rgtg=""
rgtp=""

cleanup() {
if [ "$ftags" -a -r "$ftags" ]; then rm "$ftags"; fi
if [ "$m3tags" -a -r "$m3tags" ]; then rm "$m3tags"; fi
if [ "$dtags" -a -r "$dtags" ]; then rm "$dtags"; fi
}

die_filenotfound() {
print_filenotfound "$*"
exit 1
}

die_directoryfiletype() {
print_self
printf "%b must specify a filetype " "$_error" >&2
printf "to operate on a directory!%b\n" "$_clear" >&2
exit 1
}

die_nofile() {
print_nofile
exit 1
}

die_unknownfiletype() {
shortname="$(basename "$*")"
print_unknownfiletype "$shortname"
exit 1
}

is_flac() { test_mimetype.sh -t "audio/flac" "$@" ; }

is_mp3() {
if test_mimetype.sh -t "audio/mpeg" "$@"; then
return 0
else
# test file extension
tmpname="$(basename "$@" ".mp3")"
tmpname2="${@##$tmpname}"
if [ "$tmpname2" = ".mp3" ]; then
return 0
fi
fi
return 1
}

flac_readtags() {
local flacname="$*"
ftags=$(mktemp ${TMPDIR:-/tmp}/${self}.ftags.XXXXXXXX)
metaflac --no-utf8-convert --export-tags-to="$ftags" "$flacname"
while read l; do
k=$(printf "%s" "$l" | cut -d '=' -f 1)
# convert ':' into '_' because ':' will mess up mp3 comments.
# shows up most often in URLs; example: 'visit http://'... comments
v=$(printf "%s" "$l" | cut -d '=' -f 2- | tr ':' '_')
case $k in
album|ALBUM) albumtitle="$v" ;;
albumartist|ALBUMARTIST) albumartist="$v" ;;
artist|ARTIST) artist="$v" ;;
comment|COMMENT) comment="$v" ;;
bpm|BPM) bpm="$v" ;;
date|DATE) year="$v" ;;
genre|GENRE) genre="$v" ;;
composer|COMPOSER) composer="$v" ;;
performer|PERFORMER) performer="$v" ;;
initialkey|INITIALKEY) initialkey="$v" ;;
title|TITLE) tracktitle="$v" ;;
tracknumber|TRACKNUMBER) tracknumber="$v" ;;
tracktotal|TRACKTOTAL) tracktotal="$v" ;;
discnumber|DISCNUMBER) discnumber="$v" ;;
disctotal|DISCTOTAL) disctotal="$v" ;;
replaygain_reference_loudness|REPLAYGAIN_REFERENCE_LOUDNESS)
rgrl="$v"
;;
replaygain_album_gain|REPLAYGAIN_ALBUM_GAIN) rgag="$v" ;;
replaygain_album_peak|REPLAYGAIN_ALBUM_PEAK) rgap="$v" ;;
replaygain_track_gain|REPLAYGAIN_TRACK_GAIN) rgtg="$v" ;;
replaygain_track_peak|REPLAYGAIN_TRACK_PEAK) rgtp="$v" ;;
esac
unset k v
done < $ftags
rm $ftags
}

flac_setartist() {
metaflac --remove-tag=ARTIST --set-tag="ARTIST=$artist" "$*"
}

flac_settracktitle() {
metaflac --remove-tag=TITLE --set-tag="TITLE=$tracktitle" "$*"
}

flac_setalbumtitle() {
metaflac --remove-tag=ALBUM --set-tag="ALBUM=$albumtitle" "$*"
}

flac_setyear() {
metaflac --remove-tag=DATE --set-tag="DATE=$year" "$*"
}

flac_setgenre() {
metaflac --remove-tag=GENRE --set-tag="GENRE=$genre" "$*"
}

flac_setcomposer() {
metaflac --remove-tag=COMPOSER --set-tag="COMPOSER=$composer" "$*"
}

flac_setperformer() {
metaflac --remove-tag=PERFORMER --set-tag="PERFORMER=$performer" "$*"
}

flac_setbpm() {
metaflac --remove-tag=BPM --set-tag="BPM=$bpm" "$*"
}

flac_setinitialkey() {
metaflac --remove-tag=INITIALKEY --set-tag="INITIALKEY=$initialkey" "$*"
}

flac_setalbumartist() {
metaflac --remove-tag=ALBUMARTIST --set-tag="ALBUMARTIST=$albumartist" "$*"
}

flac_settracknumber() {
metaflac --remove-tag=TRACKNUMBER --set-tag="TRACKNUMBER=$tracknumber" "$*"
}

flac_settracktotal() {
metaflac --remove-tag=TRACKTOTAL --set-tag="TRACKTOTAL=$tracktotal" "$*"
}

flac_setdiscnumber() {
metaflac --remove-tag=DISCNUMBER --set-tag="DISCNUMBER=$discnumber" "$*"
}

flac_setdisctotal() {
metaflac --remove-tag=DISCTOTAL --set-tag="DISCTOTAL=$disctotal" "$*"
}

flac_setcomment() {
metaflac --remove-tag=COMMENT --set-tag="COMMENT=$comment" "$*"
}

mp3_readtags() {
local mp3name="$*"
m3tags=$(mktemp ${TMPDIR:-/tmp}/${self}.m3tags.XXXXXXXX)
mid3v2 --list "$mp3name" > $m3tags
while read l; do
k=$(printf "%s" "$l" | cut -d '=' -f 1)
# convert ':' into '_' because ':' will mess up mp3 comments.
# shows up most often in URLs; example: 'visit http://'... comments
v=$(printf "%s" "$l" | cut -d '=' -f 2- | tr ':' '_')
case $k in
TALB) albumtitle="$v" ;;
TPE2) albumartist="$v" ;;
TPE1) artist="$v" ;;
COMM) comment="$v" ;;
TBPM) bpm="$v" ;;
TYER) year="$v" ;;
TDRC) year="$v" ;;
TCON) genre="$v" ;;
TCOM) composer="$v" ;;
TOPE) performer="$v" ;;
TKEY) initialkey="$v" ;;
TIT2) tracktitle="$v" ;;
TRCK)
tracknumber="$(printf "%s\n" "$v" | cut -d'/' -f 1)"
tracktotal="$(printf "%s\n" "$v" | cut -d'/' -f 2)"
;;
TPOS)
discnumber="$(printf "%s\n" "$v" | cut -d'/' -f 1)"
disctotal="$(printf "%s\n" "$v" | cut -d'/' -f 2)"
;;
TXXX)
k2="$(printf "%s\n" "$v" | cut -d'=' -f 1)"
v2="$(printf "%s\n" "$v" | cut -d'=' -f 2-)"
case $k2 in
replaygain_reference_loudness) rgrl="$v2" ;;
replaygain_album_gain) rgag="$v2" ;;
replaygain_album_peak) rgap="$v2" ;;
replaygain_track_gain) rgtg="$v2" ;;
replaygain_track_peak) rgtp="$v2" ;;
esac
unset k2 v2
;;
esac
unset k v
done < $m3tags
rm $m3tags
}

mp3_setartist() {
mid3v2 --artist="$artist" "$*"
}

mp3_settracktitle() {
mid3v2 --song="$tracktitle" "$*"
}

mp3_setalbumtitle() {
mid3v2 --album="$albumtitle" "$*"
}

mp3_setyear() {
mid3v2 --year="$year" "$*"
}

mp3_setgenre() {
mid3v2 --genre="$genre" "$*"
}

mp3_setcomposer() {
mid3v2 --TCOM "$composer" "$*"
}

mp3_setperformer() {
mid3v2 --TOPE "$performer" "$*"
}

mp3_setbpm() {
mid3v2 --TBPM "$bpm" "$*"
}

mp3_setinitialkey() {
mid3v2 --TKEY "$initialkey" "$*"
}

mp3_setalbumartist() {
mid3v2 --TPE2 "$albumartist" "$*"
}

mp3_settracknumber() {
mid3v2 --track="$tracknumber/$tracktotal" "$*"
}

mp3_settracktotal() {
mid3v2 --track="$tracknumber/$tracktotal" "$*"
}

mp3_setdiscnumber() {
mid3v2 --TPOS="$discnumber/$disctotal" "$*"
}

mp3_setdisctotal() {
mid3v2 --TPOS="$discnumber/$disctotal" "$*"
}

mp3_setcomment() {
mid3v2 -c "$comment" "$*"
}

tag_edit_change_value() {
local tag=$1
if [ -z "$do_tag_new_value" ]; then
eval val="\"\$${tag}\""
print_self
printf "%bcurrent %s: %b%s" "$_norm" "$tag" "$_high" "$val"
printf "%b\n" "$_clear"
print_self
printf "%benter new %s value:%b " "$_norm" "$tag" "$_clear"
eval read \$tag
eval val="\"\$${tag}\""
print_self
printf "%bcurrent %s: %b%s" "$_norm" "$tag" "$_high" "$val"
printf "%b\n" "$_clear"
else
eval $tag="\"\$do_tag_new_value\""
eval val="\"\$${tag}\""
print_self
printf "%b%s changed to: %s%b\n" "$_norm" "$tag" "$val" "$_clear"
fi
}

tag_edit() {
arg=$1 && shift
local editname="$*"
local tag=""
case $arg in
artist|ARTIST) tag=artist ;;
tracktitle|TRACKTITLE) tag=tracktitle ;;
albumtitle|ALBUMTITLE) tag=albumtitle ;;
date|DATE|year|YEAR) tag=year ;;
genre|GENRE) tag=genre ;;
composer|COMPOSER) tag=composer ;;
performer|PERFORMER) tag=performer ;;
bpm|BPM) tag=bpm ;;
key|KEY|initialkey|INITIALKEY) tag=initialkey ;;
albumartist|ALBUMARTIST) tag=albumartist ;;
tracknumber|TRACKNUMBER) tag=tracknumber ;;
tracktotal|TRACKTOTAL) tag=tracktotal ;;
discnumber|DISCNUMBER) tag=discnumber ;;
disctotal|DISCTOTAL) tag=disctotal ;;
comment|COMMENT) tag=comment ;;
esac
eval set_func=\${filetype}_set\${tag}
tag_edit_change_value $tag
$set_func "$editname"
unset tag
}

tag_print() {
# variable name:display text:display text field width:string output length
displaytags="artist:artist name:21:43
tracktitle:track title:21:43
albumtitle:album title:21:43
year:date:21:43
genre:genre:21:43
composer:composer:21:43
performer:performer:21:43
bpm:bpm:21:43
initialkey:key:21:43
albumartist:album artist:21:43
tracknumber:track number:21:43
tracktotal:total tracks:21:43
discnumber:disc number:21:43
disctotal:total discs:21:43
comment:comment:21:43
rgag:replaygain_album_gain:21:43
rgap:replaygain_album_peak:21:43
rgtg:replaygain_track_gain:21:43
rgtp:replaygain_track_peak:21:43
rgrl:replaygain_reference_loudness:21:35"
dtags=$(mktemp ${TMPDIR:-/tmp}/${self}.dtags.XXXXXXXX)
printf "%s\n" "$displaytags" > "$dtags"

zIFS="$IFS" && IFS=":"
while read var pn pnfw prec; do
eval val="\"\$${var}\""
if [ "$val" ]; then
print_self
printf "%b%${pnfw}s:%b " "$_norm" "$pn" "$_high"
printf "%.${prec}s%b\n" "$val" "$_clear"
else
missing_tags="${missing_tags}${var} "
fi
done < "$dtags"
rm $dtags
IFS="$zIFS" && unset zIFS

if [ "$missing_tags" ]; then
print_self
printf "%bmissing%b %s%b\n" "$_norm" "$_error" "$missing_tags" "$_clear"
fi
}

tag_read() {
case $filetype in
flac) flac_readtags "$*" ;;
mp3) mp3_readtags "$*" ;;
esac
}

do_file() {
[ ! -f "$*" ] && die_filenotfound "$*"

if [ "$filetype" ]; then
# TODO FIXME
print_self
printf "%bnoop%b\n" "$_norm" "$_clear"
elif is_flac "$*"; then
filetype="flac" && fileext="flac"
elif is_mp3 "$*"; then
filetype="mp3" && fileext="mp3"
else
die_unknownfiletype "$*"
fi

print_self
printf "%bfile: %b%s%b\n" "$_norm" "$_high" "$*" "$_clear"

tag_read "$*"
[ "$do_tag_print" ] && tag_print "$*"
[ "$do_tag_edit" ] && tag_edit "$do_tag_edit" "$*"
}

while getopts "3e:fhk:pv:V?" option; do
case $option in
3) filetype="mp3" && fileext="mp3" ;;
e) do_tag_edit="$OPTARG" ;;
f) filetype="flac" && fileext="flac" ;;
h) print_help && exit ;;
k) do_tag_edit="$OPTARG" ;;
p) do_tag_print="Y" ;;
v) do_tag_new_value="$OPTARG" ;;
V) print_version && exit ;;
"?") print_help && exit ;;
esac
shift $((OPTIND - 1))
done

[ $# -lt 1 ] && die_nofile
[ ! -e "$*" ] && die_filenotfound "$*"

trap cleanup EXIT

if [ -d "$*" ]; then
[ -z "$fileext" ] && die_directoryfiletype

for f in "$*"/*.$fileext; do
printf "%s\n" "$f"
do_file "$f"
done
else
do_file "$*"
fi

exit 0

Return to top of this page or return to the overview of this repository