#!/bin/bash # # easyGG - an automatic gallery generator # # Copyright (c) 2018 Stephan Uhlmann <su@su2.info> # # /************************************************************************ # * This program is free software: you can redistribute it and/or modify * # * it under the terms of the GNU General Public License as published by * # * the Free Software Foundation, either version 3 of the License, or * # * (at your option) any later version. * # * * # * See http://www.gnu.org/licenses/gpl.txt for the full text of the GPL. * # ************************************************************************/ # #### Default parameters # Tools needed # specify full path when not in $PATH JHEAD="jhead" CONVERT="convert" MKDIR="mkdir" FIND="find" GREP="grep" SED="sed" BASENAME="basename" DU="du" SORT="sort" # title of each page # can be changed with --title option TITLE="$(${BASENAME} "$(pwd)")" # html which will be added on top of the page # can be changed with --headerhtml option HEADERHTML="" # working directory # all files will be put in a subdirectory of this name # can be changed with --subdir option SUBDIR="gallery" # 0=debug (lots of info for debugging) # 1=info (default, normal progress info) # 2=error (program can continue) # 3=critical (program can't continue) # can be changed with --verbose and --qiet option VERBOSELEVEL=1 # max dimension of thumbnail image # no side (width/height) will be longer than this. but image won't be enlarged # if it is smaller. aspect ratio will be kept. # can be changed with the --thumb-dimension option THUMB_DIM="160" # max dimension of full image # no side (width/height) will be longer than this. but image won't be enlarged # if it is smaller. aspect ratio will be kept. # can be changed with the --full-dimension option FULL_DIM="800" #### Functions # $1 file htmlclear() { msg 0 "Creating HTML file for ${1}." echo -n > "${SUBDIR}/${1}.html" || msg 3 "Error creating HTML file. Stop." } # $1 string, $2 file htmlprint() { echo -e -n "${1}" >> "${SUBDIR}/${2}.html" || msg 3 "Error writing HTML file. Stop." } # $1 title, $2 file htmlheader() { msg 0 "Writing HTML header for ${2}." htmlprint "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\\n" "${2}" htmlprint "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\\n" "${2}" htmlprint "<head>\\n" "${2}" htmlprint "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\\n" "${2}" htmlprint "<meta http-equiv=\"Generator\" content=\"easyGG\"/>\\n" "${2}" htmlprint "<title>${1}</title>\\n" "${2}" htmlprint "<style type=\"text/css\">\\n" "${2}" htmlprint "<!--\\n" "${2}" htmlprint " body { color: #000000; background-color: #dddddd; font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; font-size: 15px; }\\n" "${2}" htmlprint " a:link { color: #000000; text-decoration: none; }\\n" "${2}" htmlprint " a:active { color: #000000; text-decoration: none; }\\n" "${2}" htmlprint " a:visited { color: #222222; text-decoration: none; }\\n" "${2}" htmlprint " a:hover { color: #000000; background-color: #bbbbbb; text-decoration: none; }\\n" "${2}" htmlprint " img { border: 1px solid black; }\\n" "${2}" htmlprint " span.hide { visibility: hidden; }\\n" "${2}" htmlprint " div.main { display: table; margin: 0 auto 0 auto; text-align: center; }\\n" "${2}" htmlprint " div.footer { display: block; clear: left; padding-top: 96px; color: #444444; font-size: small; }\\n" "${2}" htmlprint "-->\\n" "${2}" htmlprint "</style>\\n" "${2}" htmlprint "</head>\\n" "${2}" htmlprint "<body>\\n" "${2}" htmlprint "${HEADERHTML}\\n" "${2}" } # $1 file htmlfooter() { msg 0 "Writing HTML footer for ${1}." htmlprint "<div class=\"footer\">\\n" "${1}" htmlprint "</div>\\n" "${1}" htmlprint "</body>\\n" "${1}" htmlprint "</html>\\n" "${1}" } # $1 severity, $2 message # | # `-> 0=debug,1=info,2=error,3=critical msg() { if [ "${1}" -ge "${VERBOSELEVEL}" ]; then echo "${2}"; fi if [ "${1}" -ge 3 ]; then exit 1; fi } # $1 tool toolcheck() { X=$(type -p "${1}") if test "${X}" && test -x "${X}"; then return 0 else return 1 fi } usage() { cat << EOF Usage: easygg [OPTIONS] This program can be called without any options. It will generate a subdirectory with a ready to use gallery. You can override some default options though. -t, --title change title of gallery (default is current directory name) -s, --subdir subdirectory in which the gallery files are written (default is "${SUBDIR}") -td, --thumb-dimension max dimension of thumbnail image (in pixel, default is ${THUMB_DIM}) no side (width/height) will be longer than this. but image won't be enlarged if it is smaller. -fd, --full-dimension max dimension of full image (in pixel, default is ${FULL_DIM}) no side (width/height) will be longer than this. but image won't be enlarged if it is smaller. -H, --header HTML code to be inserted on top of each page (default: none) -v, --verbose show more debugging info -q, --quiet show only errors -h, --help shows this information EOF } #### Main # command line arguments while [ "${#}" -gt 0 ] ; do case "${1}" in --title|-t) TITLE="${2}" shift ;; --subdir|-s) SUBDIR="${2}" shift ;; --thumb-dimension|-td) THUMB_DIM="${2}" shift ;; --full-dimension|-fd) FULL_DIM="${2}" shift ;; --header|-H) HEADERHTML="${2}" shift ;; --verbose|-v) VERBOSELEVEL=0 ;; --quiet|-q) VERBOSELEVEL=2 ;; --help|-help|-h) usage exit 0 ;; *) msg 3 "Unknown option: ${1}" ;; esac shift done # All tools there? toolcheck ${JHEAD} || msg 3 "jhead not found." toolcheck ${CONVERT} || msg 3 "convert not found." toolcheck ${MKDIR} || msg 3 "mkdir not found." toolcheck ${FIND} || msg 3 "find not found." toolcheck ${GREP} || msg 3 "grep not found." toolcheck ${SED} || msg 3 "sed not found." toolcheck ${BASENAME} || msg 3 "basename not found." toolcheck ${DU} || msg 3 "du not found." toolcheck ${SORT} || msg 3 "sort not found." # find files and map them into an array mapfile -t FILES < <(${FIND} . -maxdepth 1 \( -iregex ".*\\.jpe?g$" -o -iregex ".*\\.png$" \) -type f -print | ${SORT} -f) if [ ${#FILES[@]} -gt 0 ]; then msg 1 "Found ${#FILES[@]} image files." else msg 3 "No image files found. Stop." fi ${MKDIR} -p "${SUBDIR}" || msg 3 "Error creating subdirectory. Stop." htmlclear index htmlheader "${TITLE}" index htmlprint "<h1>${TITLE}</h1>\\n" index htmlprint "<div class=\"main\">\\n" index htmlprint "<table cellspacing=\"5\" cellpadding=\"5\">\\n" index htmlprint "<tr>\\n" index # need to precompute filenames because they are referenced back/ahead in the prev/next links for (( i=0; i < ${#FILES[@]}; i++ )); do FILENAMES[${i}]=$(${BASENAME} "${FILES[${i}]}" | ${SED} s/\ /_/g); done for (( i=0; i < ${#FILES[@]}; i++ )) do FILE="${FILES[${i}]}" FILENAME="${FILENAMES[${i}]}" msg 1 "$((i+1))/${#FILES[@]} ${FILE}" htmlprint "<td align=\"center\" valign=\"bottom\">" index CONVERT_OPTIONS=(-strip -quality 90) # look in exif if image needs rotation ORIENTATION=$(${JHEAD} "${FILE}" | ${GREP} "Orientation") if [ "${ORIENTATION}" ]; then X=$(echo "${ORIENTATION}" | ${SED} s/.*rotate\ //) CONVERT_OPTIONS+=( -rotate "${X}" ) fi ${CONVERT} "${CONVERT_OPTIONS[@]}" -scale "${THUMB_DIM}x${THUMB_DIM} >" "${FILE}" "${SUBDIR}/thumb_${FILENAME}" || msg 3 "Error converting thumbnail." NEWDIM_THUMB_X=$(${JHEAD} "${SUBDIR}/thumb_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/\ x.*$// | ${SED} s/^.*:\ //) NEWDIM_THUMB_Y=$(${JHEAD} "${SUBDIR}/thumb_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/^.*x\ //) msg 0 "scaled thumb image to ${NEWDIM_THUMB_X} x ${NEWDIM_THUMB_Y}" ${CONVERT} "${CONVERT_OPTIONS[@]}" -scale "${FULL_DIM}x${FULL_DIM} >" "${FILE}" "${SUBDIR}/pic_${FILENAME}" || msg 3 "Error converting full pic." NEWDIM_FULL_X=$(${JHEAD} "${SUBDIR}/pic_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/\ x.*$// | ${SED} s/^.*:\ //) NEWDIM_FULL_Y=$(${JHEAD} "${SUBDIR}/pic_${FILENAME}" | ${GREP} "Resolution" | ${SED} s/^.*x\ //) msg 0 "scaled full image to ${NEWDIM_FULL_X} x ${NEWDIM_FULL_Y}" htmlprint "<a href=\"page_${FILENAME}.html\"><img src=\"thumb_${FILENAME}\" width=\"${NEWDIM_THUMB_X}\" height=\"${NEWDIM_THUMB_Y}\" alt=\"${FILENAME}\"/></a><br/>" index htmlprint "${FILENAME}" index # generate sub-pages htmlclear "page_${FILENAME}" htmlheader "${TITLE}" "page_${FILENAME}" htmlprint "<h2>${TITLE}</h2>\\n" "page_${FILENAME}" htmlprint "<div class=\"main\">\\n" "page_${FILENAME}" # navigation X="<— [prev] " if [ ${i} -gt 0 ]; then htmlprint "<a href=\"page_${FILENAMES[$((i-1))]}.html\">${X}</a>" "page_${FILENAME}" else htmlprint "<span class=\"hide\">${X}</span>" "page_${FILENAME}" fi htmlprint "<a href=\"index.html\"> [index] </a>" "page_${FILENAME}" X=" [next] —>" if [ $((i+1)) -lt ${#FILES[@]} ]; then htmlprint "<a href=\"page_${FILENAMES[$((i+1))]}.html\">${X}</a>\\n" "page_${FILENAME}" else htmlprint "<span class=\"hide\">${X}</span>\\n" "page_${FILENAME}" fi # table with image and info INFOS_SIZE="$(${DU} -k "${SUBDIR}/pic_${FILENAME}" | ${GREP} -o '^[0-9]*')" INFOS_DATETIME="$(${JHEAD} "${FILE}" | ${GREP} -e 'Date/Time')" INFOS_SETTINGS="$(${JHEAD} "${FILE}" | ${GREP} -e 'Flash' -e 'Focal length' -e 'Exposure time' -e 'Aperture' | ${SED} 's/$/\<br\/\>/g')" htmlprint "<table>\\n" "page_${FILENAME}" htmlprint "<tr><td colspan=\"2\">\\n" "page_${FILENAME}" htmlprint "<img src=\"pic_${FILENAME}\" width=\"${NEWDIM_FULL_X}\" height=\"${NEWDIM_FULL_Y}\" alt=\"${FILENAME}\"/>\\n" "page_${FILENAME}" htmlprint "</td></tr>\\n" "page_${FILENAME}" htmlprint "<tr>\\n<td align=\"left\">" "page_${FILENAME}" htmlprint "<small>File : ${FILENAME} ($((i+1))/${#FILES[@]})<br/>Size : ${INFOS_SIZE} KB<br/>${INFOS_DATETIME}</small>" "page_${FILENAME}" htmlprint "</td>\\n<td align=\"left\">" "page_${FILENAME}" htmlprint "<small>${INFOS_SETTINGS}</small>" "page_${FILENAME}" htmlprint "</td>\\n</tr>\\n</table>\\n" "page_${FILENAME}" htmlprint "</div>\\n" "page_${FILENAME}" htmlfooter "page_${FILENAME}" htmlprint "</td>\\n" index if [ $((i%5)) -eq 4 ]; then if [ $((i+1)) -ne ${#FILES[@]} ]; then htmlprint "</tr>\\n<tr>\\n" index fi fi done htmlprint "</tr>\\n" index htmlprint "</table>\\n" index htmlprint "</div>\\n" index htmlfooter index msg 1 "Finished."