#!/bin/bash # # Edit Ogg and FLAC comment fields with the default text editor ($EDITOR) # # If multiple arguments are given, only the common fields are edited. # Any fields added while editing override what's in the files. # # Usage: vorbiscommentedit [file [file [file...] ] ] # # To do: Handle fields with newlines in the value. # # Author: Bert Bos # Created: 17 Nov 2007 # Version: 1.0 (25 Nov 2007) # Differences between Linux and BSD: # if stat -c "%Y" . >/dev/null 2>&1; then function mystat { stat -L -c '%Y %s %D %F %i' "$@"; } # Linux else function mystat { stat -L -f '%m %z %d %p %i' "$@"; } # BSD fi # get-tags -- extract tags from Ogg or FLAC function get-tags { # Using "file" is safer than the file extension, but much slower... # case `file "$1"` in # "$1: "Ogg*Vorbis*) vorbiscomment -R -l "$1";; # "$1: "FLAC*) metaflac --no-utf8-convert --export-tags-to=- "$1";; # *) echo "Error: file is neither Ogg Vorbis nor FLAC: $1" >&2;; # esac case "$1" in *.ogg|*.ogx|*.ogv|*.oga|*.spx) vorbiscomment -R -l "$1";; *.flac) metaflac --no-utf8-convert --export-tags-to=- "$1";; *) echo "Error: file is neither Ogg Vorbis nor FLAC: $1" >&2;; esac } # set-tags -- replace tags in Ogg or FLAC with those from stdin function set-tags { # Using "file" is safer than the file extension, but much slower... # case `file "$1"` in # "$1: "Ogg*Vorbis*) vorbiscomment -R -w -c - "$1";; # "$1: "FLAC*) metaflac --remove-all-tags --no-utf8-convert \ # --import-tags-from=- "$1";; # *) echo "Error: file is neither Ogg Vorbis nor FLAC: $1" >&2;; # esac case "$1" in *.ogg|*.ogx|*.ogv|*.oga|*.spx) vorbiscomment -R -w -c - "$1";; *.flac) metaflac --remove-all-tags --no-utf8-convert \ --import-tags-from=- "$1";; *) echo "Error: file is neither Ogg Vorbis nor FLAC: $1" >&2;; esac } # one_line_per_key -- put multivalued fields on one line, separated by tab function one_line_per_key { awk -F = ' { key = toupper($1) if (!curline) {curkey = key; curline = $0} else if (key == curkey) {curline = curline " " $0} else {print curline; curkey = key; curline = $0} } END {if (curline) print curline}' } [[ -z "$1" ]] && exit trap 'rm -f $COMMON $OTHERTAGS $TMP1 $TMP2 $TMP3' 0 COMMON=`mktemp` || exit 1 OTHERTAGS=`mktemp` || exit 1 TMP1=`mktemp` || exit 1 TMP2=`mktemp` || exit 1 TMP3=`mktemp` || exit 1 # Get all common fields of the given files into $COMMON # Get the tags of all non-common fields into $OTHERTAGS # first="$1" shift echo -e ".\c" get-tags "$first" | sort -fu | one_line_per_key >$COMMON for f; do echo -e ".\c" get-tags "$f" | sort -fu | one_line_per_key >$TMP1 comm -12 $COMMON $TMP1 >$TMP2 cut -d= -f1 <(comm -23 $COMMON $TMP1) <(comm -13 $COMMON $TMP1) \ | sort -fu - $OTHERTAGS >$TMP3 mv $TMP2 $COMMON mv $TMP3 $OTHERTAGS done echo -e " \c" # Add the fields that weren't common, prefixed with a "~", so the user # can see what other fields exist. Then let the user edit. Exit if # user made no edits. Otherwise, remove "~" prefixed fields and # empty lines. # cat <(tr '\t' '\n' <$COMMON) <(sed -e 's/^/~ /' $OTHERTAGS) >$TMP1 \ && mv $TMP1 $COMMON s=`mystat $COMMON` ${EDITOR:-editor} $COMMON [[ "$s" = `mystat $COMMON` ]] && echo && exit egrep -v '^$|^~' $COMMON >$TMP1 mv $TMP1 $COMMON # Check if result is reasonably well-formed # LC_ALL=C egrep -q -v '^[ -}]+=' $COMMON \ && echo "Error: malformed tags" >&2 && exit 2 # If the user edited fields that existed, but were previously not # common to the ogg files, remove their tags from OTHERTAGS (the list of # tags that were not common), because the user wants them to become # common. # for tag in $(cut -d= -f1 $COMMON | sort -fu); do egrep -v -i "^$tag\$" $OTHERTAGS >$TMP1 mv $TMP1 $OTHERTAGS done # Put modified fields back. Copy all fields that were not in the set # that was edited (and should therefore not be touched), concatenate # the result with the edited set, and write the result into the Ogg # file. # echo -e ".\c" get-tags "$first" >$TMP1 for tag in $(< $OTHERTAGS); do egrep -i "^$tag=" $TMP1; done >$TMP2 cat $COMMON $TMP2 | set-tags "$first" for f; do echo -e ".\c" get-tags "$f" >$TMP1 for tag in $(< $OTHERTAGS); do egrep -i "^$tag=" $TMP1; done >$TMP2 cat $COMMON $TMP2 | set-tags "$f" done echo