Symlink notifyd.py to osd_server.py for backward compatibility
[grml-scripts.git] / usr_bin / twebgal
1 #!/bin/bash
2 # Filename:      twebgal
3 # Purpose:       create a tiny webgallery using CSS-features
4 # Authors:       Wolfgang Scheicher <worf@grml.org>
5 # Bug-Reports:   see http://grml.org/bugs/
6 # License:       This file is licensed under the GPL v2.
7 ################################################################################
8
9 # Defaults
10 DISPLAY_GEOMETRY=640x480
11 THUMBNAIL_GEOMETRY=150x150
12 PREVIEW_GEOMETRY=250x250
13 THUMBNAIL_QUALITY=75
14
15 PARENT_DIR="Übergeordnetes Verzeichnis"
16
17 PATH=$PATH:/usr/X11R6/bin:/usr/bin
18 DATADIR=".tmp"
19 LOG=".tmp/log"
20 VERBOSE="/dev/null"
21 OPTS=$*
22 OLDIFS=$IFS
23 IFS="
24 "
25
26 THUMBNAILS=("$DISPLAY_GEOMETRY" "$THUMBNAIL_GEOMETRY")
27 FILE_EXTENSIONS=("jpg" "png" "gif")
28 REQUIRED=("jpegtran" "convert" "identify" "find" "tr" "sed" "awk" "grep")
29
30 ##############################################################
31
32 create_sample_config () {
33 # sample config with some default values
34 #
35 if [ -f description ] ; then
36   DST=/dev/stdout
37   echo "description file allready exists!"
38 else
39   echo "creating example description file"
40   DST="./description"
41 fi
42 echo -e "# Gallery Config File\n#
43 # Title for this Directory
44 TITLE=\"${PWD##*/}\"\n
45 # Optional Comment
46 COMMENT=\"\"\n
47 # this parameters affect the dir list in the parent directory:
48 # thumbnail to show
49 THUMB=\"$(find_all_images | head -n 1 )\"\n
50 # Index to sort dirs by.
51 # You might want to use some date for chronological order
52 INDEX=\"$(date +%F)\"\n" > $DST
53 if [[ -z $STYLESHEET ]] ; then
54 echo -e "# Stylesheet
55 # This will be used for all subdirs as well
56 STYLESHEET=\"stylesheet.css\"" >> $DST
57 fi
58 }
59
60 ##############################################################
61
62 init_gallery () {
63 # create datadir and check for required apps
64 #
65   mkdir -p ${DATADIR}
66   echo -n "" > ${LOG}
67   for app in "${REQUIRED[@]}" ; do
68     if which $app 2>/dev/null >/dev/null ; then
69       true
70     else
71       echo "ERROR: $app not found!"
72       exit;
73     fi
74   done
75   if [[ -f description ]] ; then source "description"; fi
76   STYLESHEET=$( find_stylesheet )
77 }
78
79 parent_dir () {
80 # adds a "../ if it is not a absolute path or a url"
81 # otherwise returns the same string again
82   if [[ "${1::1}" == "/" ]] ; then
83     echo "$1"
84   elif [[ "${1::7}" == "http://" ]] ; then
85     echo "$1"
86   elif [[ ! -z $1 ]] ; then
87     echo "../${1}"
88   fi
89 }
90
91 find_stylesheet () {
92 # searches parent dirs recursively for a stylesheet
93 # until it reaches the root dir
94   if [[ -f description ]] ; then source "description"; fi
95   if [[ -z $STYLESHEET ]] ; then
96     if [[ ! "$PWD" == "/" ]] ; then
97       cd ..
98       parent_dir $( find_stylesheet )
99     fi
100   else
101     echo $STYLESHEET
102   fi
103 }
104
105 ##############################################################
106
107 find_all_images() {
108 # returns a list of all (supported) Images in the current dir
109 #
110   for ext in "${FILE_EXTENSIONS[@]}" ; do
111     find *.${ext} -maxdepth 0 2> /dev/null
112   done
113 }
114
115 find_all_dirs() {
116 # Searches all dirs and lists them,
117 # sorted by the INDEX field in the description file
118 #
119   DIRS="$(find * -maxdepth 0 -type d)"
120   for DIR in $DIRS ; do
121     ( INDEX="_"
122     if [[ -f ${DIR}/description ]] ; then source "${DIR}/description"; fi
123     echo "${INDEX} ${DIR}" )
124   done | sort | sed -e "s/^\S*\s//"
125 }
126
127 ##############################################################
128
129 prune() {
130 # deletes old thumbnails and html files
131 # temporarily creates a filelist
132 #
133   if [[ "$RECURSIVE" == "true" ]] ; then
134     ALL_DIRS=( $(find_all_dirs) )
135     for DIR in "${ALL_DIRS[@]}"; do
136       ( IFS=$OLDIFS && cd "$DIR" && $0 $OPTS ; )
137     done
138   fi
139   echo "->  ${PWD}"
140   if [[ -d "${DATADIR}" ]] ; then
141     find_all_images > "${DATADIR}/filelist"
142     for GEOMETRY in "${THUMBNAILS[@]}"; do
143       (
144       [[ -d "${DATADIR}/${GEOMETRY}" ]] &&
145       cd "${DATADIR}/${GEOMETRY}" &&
146       find_all_images |
147       awk 'NR==FNR {a[$0];next}
148            !($0 in a)' ../filelist - |
149       while read line ; do
150         echo "rm ${DATADIR}/${GEOMETRY}/${line}"
151         rm "${line}"
152       done
153       )
154     done
155     (
156     cd "${DATADIR}/" &&
157     find *.html -maxdepth 0 2> /dev/null |
158     awk 'NR==FNR {a[$0];next}
159          {sub(/\.html$/,"",$0)}
160          !($0 in a)' filelist - |
161     while read line ; do
162       echo "rm ${DATADIR}/${line}.html"
163       rm "${line}.html"
164     done
165     )
166     rm "${DATADIR}/filelist"
167   fi
168 }
169
170 ##############################################################
171
172 build_thumbnail() {
173 # generates thumbnails
174 # time of modification is set to that of the original and used
175 # to check if existing thumbs need updating
176 # orientation in the exif-header is used to rotate generated images
177 #
178   for GEOMETRY in "${THUMBNAILS[@]}"; do
179     mkdir -p "$DATADIR/$GEOMETRY" || exit
180     echo -ne "($GEOMETRY"
181     DST="$DATADIR/$GEOMETRY/$FILE"
182     SKIP=false
183      if [[ -e $DST ]]; then
184       if [[ $(stat -c "%Y" $FILE) == $(stat -c "%Y" $DST) ]]; then
185         SKIP=true
186         echo -n " skip"
187       else
188         echo -n " D"
189         rm $DST
190       fi
191     else
192       echo -n "  "
193     fi
194
195     if [[ $SKIP == "false" ]]; then
196       ORIENTATION=""
197       ORIENTATION=$(identify -format "%[EXIF:Orientation]" "$FILE")
198       if [[ "$ORIENTATION" == "6" ]]; then
199         echo -n "TR"
200         convert -quality $THUMBNAIL_QUALITY -rotate 90 -geometry "$GEOMETRY+0+0>" "$FILE" "$DST"
201       elif [[ "$ORIENTATION" == "8" ]]; then
202         echo -n "TL"
203         convert -quality $THUMBNAIL_QUALITY -rotate 270 -geometry "$GEOMETRY+0+0>" "$FILE" "$DST"
204       else
205         echo -n "T "
206         convert -quality $THUMBNAIL_QUALITY -geometry "$GEOMETRY+0+0>" "$FILE" "$DST"
207       fi
208       if [[ "${FILE##*.}" == "jpg" ]]; then
209         echo -n "O"
210         jpegtran -copy none -optimize -progressive "$DST" > "$DST.tmp"
211         mv -f "$DST.tmp" "$DST"
212       else
213         echo -n " "
214       fi
215       touch -m --reference=$FILE $DST
216     fi
217     echo -n ") "
218   done
219 }
220
221 build_html() {
222 # generates the HTML
223
224   echo -n "(html"
225
226   THUMBGEOMETRY=$(identify -format "%wx%h" "$DATADIR/$THUMBNAIL_GEOMETRY/${FILE}")
227   THUMBWIDTH=${THUMBGEOMETRY%x*}
228   THUMBHEIGHT=${THUMBGEOMETRY#*x}
229
230   THUMBT=$(((156 - $THUMBHEIGHT )/2 ))
231   THUMBB=$(((157 - $THUMBHEIGHT )/2 ))
232   THUMBL=$(((156 - $THUMBWIDTH  )/2 ))
233   THUMBR=$(((157 - $THUMBWIDTH  )/2 ))
234
235   TITLE=$FILE
236   COMMENT=$(identify -format "%c" "${FILE}" | tr '\n' ' ')
237   THUMBNAIL=$DATADIR/$THUMBNAIL_GEOMETRY/${FILE}
238   THIS_HTML=$DATADIR/${FILE}.html
239
240   html_header > ${THIS_HTML}
241
242 echo "<div class=\"gallerynav\">
243 ${FILE}&nbsp;&nbsp;&nbsp;&nbsp;
244 <a href=\"../index.html\">Index</a>&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"${LAST}.html\">&lt;&lt;&lt;</a>&nbsp;${CURRENT}/${IMAGE_COUNT}&nbsp;<a href=\"${NEXT}.html\">&gt;&gt;&gt;</a>
245 </div>
246
247 <div class=\"cont\">
248 <center>
249 <a href=\"../${FILE}\"><img alt=\"${DISPLAY_GEOMETRY}/${FILE}\" src=\"${DISPLAY_GEOMETRY}/${FILE}\" /></a>
250 </center><br />
251 " >> $THIS_HTML
252
253 identify -format "<table>
254 <tr><td>Auflösung:</td><td>%wx%h</td></tr>
255 <tr><td>Dateigröße:</td><td>%b</td></tr>
256 <tr><td>Camera:</td><td>%[EXIF:Model]</td></tr>
257 <tr><td>Datum/Zeit:</td><td>%[EXIF:DateTime]</td></tr>
258 <tr><td>Kommentar:</td><td>%c</td></tr>
259 </table>" ${FILE} >> $THIS_HTML
260
261 echo "</div>" >> $THIS_HTML
262 html_foot >> $THIS_HTML
263
264 echo "<a href=\"$THIS_HTML\"><img alt=\"Thumbnail\" src=\"$THUMBNAIL\" style=\"width:${THUMBWIDTH}px; height:${THUMBHEIGHT}px; margin: ${THUMBT}px ${THUMBR}px ${THUMBB}px ${THUMBL}px;\" title=\"${COMMENT}\" /><br />$FILE</a>" >> index.html
265
266 echo -en ")"
267 }
268
269 ##############################################################
270
271 start_index () {
272 # generates the upper part of the index page
273 # containing the list of subdirs
274 # which are recursively processed from here too
275 #
276   TITLE="${TITLE=${PWD##*/}}"
277   html_header > index.html
278   echo "<div class=\"cont\">" >> index.html
279   echo "<h1>${TITLE}</h1>" >> index.html
280   echo "<ul>" >> index.html
281   echo "<li class=\"back\"><b><a href=\"../index.html\">${PARENT_DIR}</a></b></li>" >> index.html
282   ALL_DIRS=( $(find_all_dirs) )
283   for DIR in "${ALL_DIRS[@]}"; do
284   ( TITLE="${DIR}"; THUMB=""; INDEX=""; COMMENT=""
285     if [[ -f ${DIR}/description ]] ; then source "${DIR}/description"; fi
286     echo "<li class=\"dir\"><a class=\"tooltip\" href=\"${DIR}/index.html\"><b>${TITLE}</b>" >> index.html
287
288     if [[ ! "${THUMB}${COMMENT}" == "" ]]; then
289       if [[ "${COMMENT}" == "" ]]; then
290         echo "<span><table><tr><td><img alt=\"Thumbnail\" src=\"${DIR}/${DATADIR}/thumb.${THUMB##*.}\" /></tr></table></span>" >> index.html
291       elif [[ "${THUMB}" == "" ]]; then
292         echo "<span><table><tr><td>${COMMENT}</td></tr></table></span>" >> index.html
293       else
294         echo "<span><table><tr><td><img alt=\"Thumbnail\" src=\"${DIR}/${DATADIR}/thumb.${THUMB##*.}\" /></td><td></td><td></td><td>${COMMENT}</td></tr></table></span>" >> index.html
295       fi
296     fi
297     echo "</a></li>" >> index.html
298     if [[ "$RECURSIVE" == "true" ]] ; then
299       ( STYLESHEET="$(parent_dir ${STYLESHEET})"
300         IFS=$OLDIFS && cd "$DIR" && $0 $OPTS ; )
301     fi
302   )
303   done
304 echo "
305 </ul>
306 ${COMMENT}
307 <div class=\"gallery\">
308 <div class=\"spacer\"></div>
309 " >> index.html
310 }
311
312 finish_index () {
313 echo "
314 <div class=\"spacer\"></div>
315 </div>
316 </div>
317 " >> index.html
318 html_foot >> index.html
319 }
320
321 ##############################################################
322
323 create_sample_stylesheet () {
324 if [ -f stylesheet.css ] ; then
325   echo "stylesheet.css allready exists!"
326   exit 1
327 fi
328 cat << EOF > stylesheet.css
329 a {text-decoration:none;}
330
331 div.gallery img {border: 2px inset #aaa; margin:3px;}
332 div.gallery a {color:#000; border: 2px outset #aaa; text-decoration:none; text-align:center; float:left; padding:3px; margin:4px; background-color:#ddd;}
333 div.gallery a:hover {color:#333; border: 2px outset #ccc; background-color:#eee;}
334 div.spacer {clear: both;}
335 div.gallerynav {text-align: center; margin: 0px; padding:0em 0.3em 0em 0.3em;}
336
337 a.tooltip, a.tooltip:link, a.tooltip:visited, a.tooltip:active  { position: relative; }
338 a.tooltip span {display: none}
339 a:hover.tooltip span {display: block; position: absolute; top: 2.5em; left: 1.5em; z-index: 100; color: #000; padding: 3px; border:1px solid #000; background: #ffc; }
340 EOF
341 }
342
343 ##############################################################
344
345 html_header () {
346 echo "<html>
347 <head>
348 <title>${TITLE}</title>
349 <link rel=\"stylesheet\" type=\"text/css\" href=\"${STYLESHEET}\" />
350 </head>
351 <body>
352 "
353 }
354
355 html_foot () {
356 echo "</body>
357 </html>
358 "
359 }
360
361
362 ##############################################################
363
364 build_gallery() {
365 # generates the whole thing, puts everything together
366
367   ALL_IMAGES=( $(find_all_images) )
368   IMAGE_COUNT=${#ALL_IMAGES[@]}
369   start_index
370   (
371   STYLESHEET="$(parent_dir ${STYLESHEET})"
372   echo "-> ${TITLE}"
373   CURRENT="0"
374   FILE="../index"
375   for file in "${ALL_IMAGES[@]}" "../index" ; do
376     NEXT="$file"
377     if [[ "$FILE" != "../index" ]]; then
378       echo -ne "$FILE\t"
379       build_thumbnail
380       build_html
381       echo ""
382     fi
383     CURRENT="$(( ${CURRENT} + 1 ))"
384     LAST="$FILE"
385     FILE="$file"
386   done
387   if [[ ! -z "${THUMB}" ]] ; then
388     echo -ne "preview thumbnail from ${THUMB}\t"
389     THUMBNAILS=("$PREVIEW_GEOMETRY")
390     FILE="${THUMB}"
391     [ -e "${DATADIR}/thumb.${THUMB##*.}" ] && mv "${DATADIR}/thumb.${THUMB##*.}" "${DATADIR}/${PREVIEW_GEOMETRY}/${THUMB}"
392     build_thumbnail &&
393     mv "${DATADIR}/${PREVIEW_GEOMETRY}/${THUMB}" "${DATADIR}/thumb.${THUMB##*.}"
394     echo
395   fi
396   ) | tee -a $LOG >> $VERBOSE
397   finish_index
398   echo " $IMAGE_COUNT Image(s) in ${TITLE}"
399 }
400
401 ##############################################################
402
403 init_gallery
404
405 while getopts "vdsrp-:" OPT ; do
406   case $OPT in
407     v) VERBOSE=/dev/stdout
408        ;;
409     d) create_sample_config
410        exit 0
411        ;;
412     s) create_sample_stylesheet
413        exit 0
414        ;;
415     r) RECURSIVE="true"
416        ;;
417     p) prune
418        exit 0
419        ;;
420     ?) # wrong argument
421        echo -n "ERROR: Missing argument for option \"$OPTARG\". "
422        exit 1
423        ;;
424     *) # wrong option
425        echo -n "ERROR: Unknown option \"$OPTARG\". "
426        exit 1
427        ;;
428   esac
429 done
430 shift $(($OPTIND -1))
431
432 build_gallery 2>${DATADIR}/errors
433 if [ -s ${DATADIR}/errors ]; then
434   echo -e "\nWARNING: there were errors. see .tmp/errors"
435 else
436   rm .tmp/errors
437 fi
438
439 ## END OF FILE #################################################################