Teil 2: Bildgalerie mit Verzeichnis synchronisieren

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

2 Gedanken zu „Teil 2: Bildgalerie mit Verzeichnis synchronisieren“

  1. 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

Schreibe einen Kommentar