Wie ich vor einiger Zeit schon mal geschrieben habe, verwende ich auf meinem Server ein kleines Skript zum Synchronisieren von Bilddaten mit einer Webgalerie. Momentan überarbeite ich dieses Skript etwas und erweitere dabei auch die Funktionalität.
Der Hauptgrund, warum ich das Skript überarbeite ist die Performance. Mittlerweile habe ich viele große Bilder die jeden Tag kodiert werden, obwohl das gar nicht unbedingt notwendig ist (wenn das Zielbild schon vorliegt kann man sich das erneute dekodieren eigentlich sparen).
Folgende Verbesserungen habe ich schon am Skript vorgenommen:
- verbesserte Performance: Bilder werden nur mehr dekodiert, wenn sie nicht schon am Ziel vorhanden sind
- Unterstützung von Olympus RAW-Dateien (neben dem Nikon RAW-Format)
- Veränderte Parameterübergabe an UFRaw, um ein besseres Ergebnis zu erzielen
- Unterstützung von Video-Encoding. Nimmt man mit der Kamera Videos auf, so werden diese vor dem Kopieren in die Bildgalerie zu einer H264 kodierten m4v Datei umgewandelt (mit AAC Audio Stream)
- Support von Links zu Images: liegen die Dateien schon im JPG-Format vor, so werden sie nicht mehr kopiert, sondern verlinkt. Das spart dann auch eine Menge an Speicherplatz.
- Verbesserte Konfiguration: Im Skript kann man das Verhalten durch das Umsetzen von Variablen beeinflussen. So kann man beispielsweise festlegen, dass alle Dateien neu kodiert und überschrieben werden sollen oder aber auch, dass (wie bisher) JPG-Dateien nicht verlinkt, sondern kopiert werden.
- Mitgabe einer Konfiguration in Form von Kommandozeilen-Parametern: Beispielsweise kann man „–overwrite“ dem Skript mitgeben, damit dann im Overwrite-Modus gearbeitet wird
Was jetzt aber immer noch fehlt ist:
- Übernehmen von Bildentwicklungseinstellungen aus einer xmp-Datei beim Umwandeln von RAW-Dateien in das JPG-Format. Im Besonderen denke ich da an Crop, Saturation, Brightness, …
Das momentane Skript sieht so aus:
#! /bin/sh
######################################################################################
# Name: #
# copyToGallery.sh #
# Description: #
# This copies images and movies from a src directory to a destination directory. #
# Raw images are converted to jpg and movies are converted to m4v. #
######################################################################################
######################
# Command-Line Flags #
######################
. /usr/share/shflags/shflags
DEFINE_boolean 'useImage' true 'link/copy ordinary image files' 'i'
DEFINE_boolean 'useLinks' false 'link not converted files instead of copying' 'l'
DEFINE_boolean 'useMovie' true 'decode movie files' 'm'
DEFINE_boolean 'useRaw' true 'decode raw files' 'r'
DEFINE_boolean 'overwrite' false 'overwrite existing files' 'o'
DEFINE_boolean 'verbose' false 'print verbose output' 'v'
DEFINE_boolean 'debug' false 'print debug output' 'd'
DEFINE_string 'source' '`pwd`' 'source directory from which (including subfolders) files are used' 's'
FLAGS_HELP=$(printf "Copies images and movie files from a source to a destination directory.\nWhile copying, raw images are decoded to jpg and movie files are converted to h264 encoded m4v files.\nOrdinary images are linked to the destination folder.\nThe source directory structure is also generated in the destination folder.\n\nUSAGE: $0 [flags] destination")
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"
# check for destination directory
if [ $# -eq 0 ]; then
echo 'error: destination missing' >&2
flags_help
exit 1
fi
DST=$1
######################
# Options #
######################
LN_EXEC=/bin/ln
LN_OPTS="-s"
CP_EXEC=/bin/cp
CP_OPTS="-p"
FFMPEG_EXEC=/usr/bin/ffmpeg
FFMPEG_OPTS="-copyts -acodec aac -strict -2 -ar 48000 -ac 2 -vcodec libx264 -preset ultrafast -r 25 -flags +mv4 -s 720x480"
UFRAW_EXEC=/opt/ufraw/ufraw-batch
UFRAW_OPTS="--silent --wb=camera --exposure=auto --compression=100 --lensfun=auto --out-type=jpeg"
REGEXP_IMAGE="\.\(jpg\|jpeg\|png\|gif\)$"
REGEXP_RAW="\.\(nef\|orf\)$"
REGEXP_MOVIE="\.\(mts\|avi\)$"
TARGET_FORMAT_IMAGE=jpg
TARGET_FORMAT_MOVIE=m4v
######################
# Methods #
######################
echo_debug() {
if [ ${FLAGS_debug} -eq 0 -o ${FLAGS_verbose} -eq 0 ]; then
echo "$@"
fi
}
echo_verbose() {
if [ ${FLAGS_verbose} -eq 0 ]; then
echo "$@"
fi
}
file_not_exists() {
if [ $FLAGS_overwrite -eq 0 ]; then
return 0
fi
if [ -f "$1" ]; then
return 1
else
return 0
fi
}
replace_special_chars() {
STR=$1
STR=`echo $STR | sed -e "s/ //g"`
STR=`echo $STR | sed -e "s/\&/_/g"`
STR=`echo $STR | sed -e "s/[ÄÖÜäöüß]/_/g"`
STR=`echo $STR | sed -e "s/(/_/g"`
STR=`echo $STR | sed -e "s/)//g"`
echo $STR
}
extension_lowercase() {
LOWER=`echo $1 | sed -r "s/([^.]*)\$/\L\1/"`
echo $LOWER
}
link_image() {
CUR_DIR=`pwd`
IMG_LEN=${#IMG_FILES[@]}
if [ $IMG_LEN -gt 0 ]; then
echo_verbose " linking $IMG_LEN image file(s)"
for ((j = 0; j < $IMG_LEN; ++j)); do
OUTPUT_FILE=$(replace_special_chars "${IMG_FILES[$j]}")
OUTPUT_FILE=$(extension_lowercase "$OUTPUT_FILE")
if file_not_exists "$TARGET_DIR/$OUTPUT_FILE"; then
echo_debug " creating \"$OUTPUT_FILE\""
$LN_EXEC $LN_OPTS "$CUR_DIR/${IMG_FILES[$j]}" "$TARGET_DIR/$OUTPUT_FILE" 2>/dev/null
else
echo_debug " not creating \"$OUTPUT_FILE\" (already exists)"
fi
done
fi
}
copy_image() {
IMG_LEN=${#IMG_FILES[@]}
if [ $IMG_LEN -gt 0 ]; then
echo_verbose " copying $IMG_LEN image file(s)"
for ((j = 0; j < $IMG_LEN; ++j)); do
OUTPUT_FILE=$(replace_special_chars "${IMG_FILES[$j]}")
OUTPUT_FILE=$(extension_lowercase "$OUTPUT_FILE")
if file_not_exists "$TARGET_DIR/$OUTPUT_FILE"; then
echo_debug " creating \"$OUTPUT_FILE\""
$CP_EXEC $CP_OPTS "${IMG_FILES[$j]}" "$TARGET_DIR/$OUTPUT_FILE" 2>/dev/null
else
echo_debug " not creating \"$OUTPUT_FILE\" (already exists)"
fi
done
fi
}
copy_raw() {
RAW_LEN=${#RAW_FILES[@]}
if [ $RAW_LEN -gt 0 ]; then
echo_verbose " converting $RAW_LEN raw file(s)"
for ((j = 0; j < $RAW_LEN; ++j)); do
OUTPUT_FILE=$(replace_special_chars ${RAW_FILES[$j]})
OUTPUT_FILE=`echo $OUTPUT_FILE | sed -e "s/$REGEXP_RAW/\.$TARGET_FORMAT_IMAGE/i"`
if file_not_exists "$TARGET_DIR/$OUTPUT_FILE"; then
echo_debug " creating \"$OUTPUT_FILE\""
$UFRAW_EXEC $UFRAW_OPTS --output="$TARGET_DIR/$OUTPUT_FILE" "${RAW_FILES[$j]}"
else
echo_debug " not creating \"$OUTPUT_FILE\" (already exists)"
fi
done
fi
}
copy_movie() {
MOVIE_LEN=${#MOVIE_FILES[@]}
if [ $MOVIE_LEN -gt 0 ]; then
echo_verbose " converting "$MOVIE_LEN" movie file(s)"
for ((j = 0; j < $MOVIE_LEN; ++j)); do
OUTPUT_FILE=$(replace_special_chars "${MOVIE_FILES[$j]}")
OUTPUT_FILE=`echo $OUTPUT_FILE | sed -e "s/$REGEXP_MOVIE/\.$TARGET_FORMAT_MOVIE/i"`
if file_not_exists "$TARGET_DIR/$OUTPUT_FILE"; then
echo_debug " creating \"$OUTPUT_FILE\""
$FFMPEG_EXEC -i "${MOVIE_FILES[$j]}" $FFMPEG_OPTS "$TARGET_DIR/$OUTPUT_FILE" 2>/dev/null
else
echo_debug " not creating \"$OUTPUT_FILE\" (already exists)"
fi
done
fi
}
######################
# Main Method #
######################
if [ ! `whoami` = root ]; then
echo "Script can only be run as root or apache user... exiting" >&2
exit 1
fi
if [ ! -d "$FLAGS_source" ]; then
echo "Directory \"$FLAGS_source\" not found" >&2
exit 1
fi
if [ $FLAGS_overwrite -eq 0 ]; then
echo_verbose "Using overwrite mode"
CP_OPTS="$CP_OPTS -f"
FFMPEG_OPTS="$FFMPEG_OPTS -y"
LN_OPTS="$LN_OPTS -f"
UFRAW_OPTS="$UFRAW_OPTS --overwrite"
else
CP_OPTS="$CP_OPTS -u"
FFMPEG_OPTS="$FFMPEG_OPTS -n"
fi
cd "$FLAGS_source"
TMPIFS=$IFS
IFS=$'\n'
DIRS=($(find * -type d -print | sort -n))
IFS=$TMPIFS
LEN=${#DIRS[@]}
echo "Processing "$LEN" directories"
for ((i = 0; i < $LEN; ++i)); do
CDIR=$(replace_special_chars ${DIRS[$i]})
SOURCE_DIR="$FLAGS_source/${DIRS[$i]}"
TARGET_DIR="$DST/$CDIR"
echo_verbose "Processing \"$SOURCE_DIR\""
if [ ! -d "$TARGET_DIR" ]; then
echo_debug " creating directory"
mkdir -p "$TARGET_DIR"
fi
cd "$SOURCE_DIR"
TMPIFS=$IFS
IFS=$'\n'
if [ $FLAGS_useImage -eq 0 ]; then
IMG_FILES=($(find * -maxdepth 0 -iregex ".*$REGEXP_IMAGE" -print | sort -n))
fi
if [ $FLAGS_useRaw -eq 0 ]; then
RAW_FILES=($(find * -maxdepth 0 -iregex ".*$REGEXP_RAW" -print | sort -n))
fi
if [ $FLAGS_useMovie -eq 0 ]; then
MOVIE_FILES=($(find * -maxdepth 0 -iregex ".*$REGEXP_MOVIE" -print | sort -n))
fi
IFS=$TMPIFS
if [ $FLAGS_useLinks -eq 0 ]; then
link_image
else
copy_image
fi
copy_raw
copy_movie
done
chown -R apache:apache "$DST"
exit 0
EDIT (vom 03.04.2012): Es wurden noch Änderungen am Skript vorgenommen, so dass dieses nun auch mit Kommandozeilenargumenten gesteuert werden kann
Die heute hinzugefügten Kommandozeilenparameter arbeiten mit Hilfe des Skripts shFlags.
Dieses von Google entwickelte Hilfsprogramm arbeitet ähnlich wie getopt und getopts funktioniert aber Betriebssystemübergreifend und ist sehr einfach zu konfigurieren. Außerdem bietet es eine automatisch aus den Parametern generierte Hilfsfunktion an
Eine neue, verbesserte Version des Skripts gibt es in Teil 3