#!/bin/sh# ---------------------------------------------------------------------# DESCRIPTION:# Maintains an OGG Vorbis mirror of a file tree of FLAC audio files## AUTHOR:# Simon Kinsella, November 2009# Chris Quirk, January 2010## LICENSE: BSD license# WARRANTY: NONE. Not rigorously tested, but it works for me!# Use at your own risk.## ---------------------------------------------------------------------## IMPORTANT - paths to master and slave trees MUST be absolute!## Syntax:# ------# ogg-sync.sh <master-tree> <slave-tree># where <master-tree> is an absolute path to the base of the FLAC audio# tree.# The OGG mirrors will be created under a directory 'q<n>' under# absolute path <slave-tree> where n is defined by the QUALITY variable# e.g.:# ogg-sync.sh /music/flac-audio /music/ogg-mirrors# will create an OGG mirror of '/music/flac-audio' under# '/music/ogg-mirrors/q2/'## FINAL WARNING: Please back-up your files and test the operation# of this script before using day-to-day.## TODO:# 1) Make the syntax more forgiving, possibly# 2) Better error handling / recovery, the usual stuff.# 3) Find a way to dispatch encoding jobs concurrently for SMP systems.# Tried xjobs but was unsuccessful.# 4) Make stat commands portable between linux and fbsd or clean up the conditionals# 5) tag dump, reinsert all non-null tags into the new ogg as part of encoding (inserts new dependancy on metaflac)# 6) test for absolute pathsOGGENC="$(which oggenc)"SCRIPT="$(basename $0)"ARCH="$(uname)"#set quality to a value between or equal to -1 and 10.#See: http://en.wikipedia.org/wiki/VorbisQUALITY=5 #override the quality argumentif [ -z "$QUALITY" ]; thenwhile getopts ":q:" Optiondo case $Option in q ) QUALITY="${OPTARG}" if [ ${QUALITY} -lt -1 -o ${QUALITY} -gt 10 ] then echo "${SCRIPT}: Invalid Quality value '${QUALITY}'" exit 1 fi esacdoneshift $(($OPTIND - 1))fiif [ "$1" ]then if [ ! -d "$1" ] then echo "${SCRIPT}: Invalid master directory '$1'" exit 1 fi MASTER_DIR="$1"else echo "${SCRIPT}: Master directory not specified" exit 1fiif [ "$2" ]then SLAVE_DIR="$2"else echo "${SCRIPT}: Slave directory not specified" exit 1fi#=============================================================================#1) For each ogg file in the slave tree:# 1.1) Look for flac file of the same path and name in the master tree:# 1.1.1) If not found, delete the oggecho "Removing stale ogg files..."mkdir -p "${SLAVE_DIR}/q${QUALITY}"cd "${SLAVE_DIR}/q${QUALITY}"SLAVE_DIR="$(pwd)"find . -type f -name '*.ogg' | while read FILEdo if [ ! -f "${MASTER_DIR}/${FILE%.*}.flac" ] then echo "Master file ${MASTER_DIR}/${FILE%.*}.flac not found - deleting ${SLAVE_DIR}/${FILE}" rm -vf "${SLAVE_DIR}/${FILE}" fidone# 2) Look for existing manifest file in MASTER_DIR/ogg-sync-q${QUALITY}.manifestcd "${MASTER_DIR}"echo "Preparing manifests..."MANIFEST="${MASTER_DIR}/ogg-sync-q${QUALITY}.manifest"touch "${MANIFEST}"rm -f "${MANIFEST}.new" #in case the script didnt complete successfully on last invocationtouch "${MANIFEST}.new"#3) For each entry in the manifest:# 3.1) If no matching flac file exists, remove the manifest entry# 3.2) If it does, reinsert into new manifest to be parsed in step 4# Manifest lines consist of size date and filename, delimited by tabsecho "Cleaning manifest..."cat ${MANIFEST}|while IFS=$(echo '\t') read OLD_SIZE OLD_DATE FILEdo if [ -f "${FILE}" ]; then if [ "$ARCH" = "FreeBSD" ]; then NEW_SIZE="$(stat -f "%z" "${FILE}")" NEW_DATE="$(stat -f "%Sm" "${FILE}")" elif [ "$ARCH" = "Linux" ]; then NEW_SIZE="$(stat -c %s "${FILE}")" NEW_DATE="$(stat -c %y "${FILE}")" #gnu coreutils date output is ugly, check others for consistency else echo "Unknown arch: $ARCH" && exit 1 fi #if the size and date are the same, add it back to the manifest if [ "${OLD_SIZE}" = "${NEW_SIZE}" -a "${OLD_DATE}" = "${NEW_DATE}" ]; then echo "${NEW_SIZE}"'\t'"${NEW_DATE}"'\t'"${FILE}" >> "${MANIFEST}.new" fi fidonerm "${MANIFEST}"mv "${MANIFEST}.new" "${MANIFEST}"#4) For each flac file in the master tree:# 4.1) If file entry doesnt exists in the manifest (same size, date, and filename; tab delimited):# 4.1.1) Dump tags# 4.1.2) Create a new ogg w/ tags# 4.1.3) Add a manifest entryecho "Generating OGG files..."cd ${MASTER_DIR}find . -type f -name '*.flac' \| while read FILE do if [ "$ARCH" = "FreeBSD" ]; then SIZE="$(stat -f "%z" "${FILE}")" DATE="$(stat -f "%Sm" "${FILE}")" elif [ "$ARCH" = "Linux" ]; then SIZE="$(stat -c %s "${FILE}")" DATE="$(stat -c %y "${FILE}")" else echo "Unknown arch: $ARCH" && exit 1 fi #if grep exits with 1, process file, add file to manifest ENTRY="$(echo "${SIZE}"'\t'"${DATE}"'\t'"${FILE}")" grep -qF "${ENTRY}" "${MANIFEST}" if [ $? -ne 0 ]; then mkdir -p "${FILE%/*}" \ && "${OGGENC}" -Qq"${QUALITY}" -o "${SLAVE_DIR}/${FILE%.*}.ogg" "${FILE}" \ && echo "${SIZE}"'\t'"${DATE}"'\t'"${FILE}" >> "${MANIFEST}" fi doneexit 0