]> git.defcon.no Git - plsgen/blob - plsgen
f5876d42bca506c03faeb3c0ec2423407af18fcf
[plsgen] / plsgen
1 #!/usr/bin/perl
2 use Image::ExifTool;
3 use POSIX;
4 use AppConfig;
5 use Getopt::Long;
6 use strict;
7
8 # TODO: Templating of EXIF
9 # TODO: Priority/sorting of EXIF tags
10 # TODO: Possibility for hide/show EXIF
11 # TODO: RSS support? Delegate that to frontend?
12 # TODO: Clear old generated files and meta on regen
13 # TODO: Use perlmagick et. al instead of convert/jhead..
14
15 # Runtime data
16 my $title = undef;
17 my $htmlonly = 0;
18 my $configfile = undef;
19 my $halp = undef;
20 my $album_comment = undef;
21 my $doexif = 1;
22
23 GetOptions (
24 "title=s" => \$title,
25 "htmlonly!" => \$htmlonly,
26 "exif!" => \$doexif,
27 "config=s" => \$configfile,
28 "help" => \$halp
29 );
30 if ( $halp )
31 {
32 print "\nplsgen version 0.1\n";
33 print "Copyright Jon Langseth, BSD lisence\n\n";
34 print " --title='Your album title'\n";
35 print " Sets the album title. Title will be stored in .title\n";
36 print " If no title is given, it will be read from .title, if present\n";
37 print " --htmlonly\n";
38 print " Add this option to only generate HTML files\n";
39 print " No image operations will be performed with this option\n";
40 print " --noexif\n";
41 print " Forces EXIF data block not to be written to HTML output\n";
42 print " --config=/path/to/config\n";
43 print " Overrides default config file location.\n";
44 print " Default is to look for ./plsgen.cfg, then ../plsgen.cfg\n";
45 print " and finally /etc/plsgen.cfg.\n";
46 print "\n";
47 exit;
48 }
49
50 # Configuration data
51 my $config = get_config( $configfile );
52 my $full_tpl_file = $config->full_tpl_file;
53 my $index_tpl_file = $config->index_tpl_file;
54 my $css_file = $config->css_file;
55 my $navigation_script = $config->navigation_script;
56 my $columns = $config->columns;
57 my $rows = $config->rows;
58 my $thumb_pre = $config->thumb_pre;
59 my $thumb_post = $config->thumb_post;
60 my $comment_pre = $config->comment_pre;
61 my $comment_post = $config->comment_post;
62 my $idx_prev_text = $config->idx_prev_text;
63 my $idx_next_text = $config->idx_next_text;
64 my $idx_ret_text = $config->idx_ret_text;
65 my $footer_tag = $config->footer_tag;
66
67 # Get or save the title, depending..
68 if ( (not $title) && ( -f ".title" ) )
69 {
70 open TF, "<.title";
71 $title = <TF>;
72 chomp($title);
73 close TF;
74 }
75 elsif ( $title )
76 {
77 open TF, ">.title";
78 print TF $title . "\n";
79 close TF;
80 }
81
82 # Get the album comment, if available
83 if ( -f "comment.txt" )
84 {
85 open CF, "<comment.txt";
86 while (<CF>) { $album_comment .= $_; }
87 close CF;
88 }
89
90 mkdir "thumb";
91 mkdir "view";
92
93 # Glob file names to an array
94 my @images = glob("*png *.jpg *.JPG *.gif *.GIF");
95 # Keep count of total number of images
96 my $imagecount = $#images+1;
97
98 my ($current, $previous, $next);
99 my $indexcount = 1;
100 my $indexfile;
101
102 my $main_meta = "<link rel='stylesheet' href='" . $css_file . "' type='text/css' />";
103 my $navigation_header = "<script type='text/javascript' src='" . $navigation_script . "'></script>";
104
105 my $thumbs = "<div class='thumbnails'>";
106
107 my $indexes = ceil( $imagecount/($rows*$columns) );
108 my $gentime = localtime;
109
110 # In groups of N ( X rows x Y colums of index):
111 # - Step through image array:
112 for ( my $i = 1; $i <= $imagecount; $i++)
113 {
114 $current = $images[$i-1];
115 # - - Previous image is previous location in array, unless cur=first
116 $previous = $images[$i-2] if $i > 1;
117 $previous = undef if $i == 1;
118 # - - Next image is next location in array, unless cur=last.
119 $next = $images[$i] if $i < ( $imagecount);
120 $next = undef if $i >= ( $imagecount);
121
122 #Set up text-strings for template replacement
123 if ( $indexcount == 1 ) { $indexfile = "index.html"; }
124 else { $indexfile = "index" . $indexcount . ".html"; }
125 my $navscript = gen_navscript( $previous, $next, $indexfile );
126 my $position = $i . " of " . $imagecount;
127 my $prev_text = "<a href='" . $previous . ".html'><img src='thumb/" . $previous . "' /></a>";
128 my $next_text = "<a href='" . $next . ".html'><img src='thumb/" . $next . "' /></a>";
129 my $cur_index_text = "<a href='". $indexfile ."'>" . $idx_ret_text . "</a>";
130 my $current_display = "view/" . $current;
131
132 # Check for, and load comment from FILENAME.txt here..
133 my $comment = undef;
134 if ( -f $current . ".txt" )
135 {
136 open CF, "<" . $current . ".txt";
137 while (<CF>) { $comment .= $_; }
138 close CF;
139 }
140 if ( $comment )
141 {
142 $comment = $comment_pre . $comment . $comment_post;
143 }
144
145 printf ("Processing image %s: %s\n", $position, $current);
146
147 # - - If rotated according to EXIF, do rotation
148 my $exif = get_exifdata( $current );
149 if ( $exif->{'Orientation'} =~ m/rotate/i )
150 {
151 system("jhead -autorot " . $current . ">/dev/null") unless $htmlonly;
152 }
153 my $exif_text = get_exifblock($exif) if $doexif;
154
155 # - - Create thumbnail image (resize to new image)
156 system("convert " . $current . " -geometry '160x120>' thumb/" . $current) unless $htmlonly;
157 # - - Create normal display image (resize to new image)
158 system("convert " . $current . " -geometry '800x600>' view/" . $current) unless $htmlonly;
159
160 # - - Save a reference to the "primary image"
161 if ( not -f ".indeximage" )
162 {
163 open IM, ">.indeximage";
164 print IM $current;
165 close IM;
166 }
167 # - - Create full view HTML file
168 my $cur_html;
169 open TEMPLATE, "<" . $full_tpl_file or die "UNABLE TO LOAD TEMPLATE $full_tpl_file\n";
170 while (<TEMPLATE>)
171 {
172 if ( $previous ) { $_ =~ s/%\{previous\}/$prev_text/; }
173 else { $_ =~ s/%\{previous\}//; }
174
175 if ( $next ) { $_ =~ s/%\{next\}/$next_text/; }
176 else { $_ =~ s/%\{next\}//; }
177
178 $_ =~ s/%\{index\}/$cur_index_text/;
179 $_ =~ s/%\{title\}/$title/;
180 $_ =~ s/%\{main_meta\}/$main_meta/;
181 $_ =~ s/%\{navigation_script\}/$navigation_header/;
182 $_ =~ s/%\{position\}/$position/;
183 $_ =~ s/%\{current\}/$current/;
184 $_ =~ s/%\{current_display\}/$current_display/;
185 $_ =~ s/%\{comment\}/$comment/;
186 $_ =~ s/%\{exif\}/$exif_text/;
187 $_ =~ s/%\{gallery_timestamp\}/$gentime/;
188 $_ =~ s/%\{navscript\}/$navscript/;
189 $_ =~ s/%\{footer_tag\}/$footer_tag/;
190 $cur_html .= $_;
191 }
192 close TEMPLATE;
193 open HTML, ">" . $current . ".html" or die "UNABLE TO WRITE\n";
194 print HTML $cur_html;
195 close HTML;
196
197 # - - Append image thumbnail code to current index content
198 $thumbs .= $thumb_pre . "<a href='" . $current . ".html'><img src='thumb/" . $current . "' /></a>" . $thumb_post;
199 if ( $i % ($rows*$columns) == 0 )
200 {
201 $thumbs .= "</div>";
202 # - - On each Y, terminate index file/group:
203 make_index( $index_tpl_file, $indexcount, $indexes, $thumbs, $album_comment );
204 $thumbs = "<div class='thumbnails'>";
205 $indexcount++;
206 }
207 elsif ( $i % ($columns) == 0 )
208 {
209 # - - On each X, terminate index content row
210 $thumbs .= "\n</div>\n<div class='thumbnails'>";
211 }
212 }
213 $thumbs .= "</div>";
214 make_index( $index_tpl_file, $indexcount, $indexes, $thumbs, $album_comment );
215 # Done.
216
217
218 # -------------- Functions supporting above code -----------------------
219 sub make_index
220 {
221 my $tpl = shift;
222 my $idxcount = shift;
223 my $lastidx = shift;
224 my $thumbs = shift;
225 my $comment = shift;
226
227 my $gentime = localtime;
228 my $html;
229
230 my $prev_file;
231 my $prev_text;
232 my $next_file;
233 my $next_text;
234 my $indexfile;
235
236 # If index counter is 1, filename is index.html
237 $indexfile = ( $indexcount ==1 ) ? "index.html" : "index" . $idxcount . ".html";
238
239 # If index counter is > 1, add previous index link.
240 if ( $idxcount > 1 )
241 {
242 $prev_file = "index" . ($idxcount-1) . ".html";
243 $prev_file = "index.html" if ( $idxcount == 2 );
244 $prev_text = "<a href='$prev_file'>" . $idx_prev_text . "</a>";
245 }
246
247 # If current image is last in array, do not add nex-index link
248 if ( $idxcount < $lastidx )
249 {
250 $next_file = "index" . ($idxcount+1) . ".html";
251 $next_text = "<a href='" . $next_file . "'>" . $idx_next_text . "</a>";
252 }
253
254 if ( $comment )
255 {
256 $comment = $comment_pre . $comment . $comment_post;
257 }
258
259 my $position = $indexcount . " of " . $lastidx;
260 my $navscript = gen_navscript( $prev_file, $next_file );
261
262 open TEMPLATE, "<" . $tpl or die "UNABLE TO LOAD TEMPLATE\n";
263 while (<TEMPLATE>)
264 {
265 $_ =~ s/%\{previous\}/$prev_text/;
266 $_ =~ s/%\{next\}/$next_text/;
267 $_ =~ s/%\{title\}/$title/;
268 $_ =~ s/%\{comment\}/$comment/;
269 $_ =~ s/%\{position\}/$position/;
270 $_ =~ s/%\{main_meta\}/$main_meta/;
271 $_ =~ s/%\{navigation_script\}/$navigation_header/;
272 $_ =~ s/%\{thumbnails\}/$thumbs/;
273 $_ =~ s/%\{gallery_timestamp\}/$gentime/;
274 $_ =~ s/%\{navscript\}/$navscript/;
275 $_ =~ s/%\{footer_tag\}/$footer_tag/;
276 $html .= $_;
277 }
278 close TEMPLATE;
279 open HTML, ">" . $indexfile or die "UNABLE TO WRITE\n";
280 print HTML $html;
281 close HTML;
282
283 }
284
285 sub get_exifdata ($)
286 {
287 my $image = shift;
288 my $exifTool = new Image::ExifTool;
289 $exifTool->Options(Unknown => 1);
290
291 my $exif;
292 my $info = $exifTool->ImageInfo($image);
293 $exif->{'Make'} = $info->{'Make'};
294 $exif->{'Model'} = $info->{'Model'};
295 $exif->{'Orientation'} = $info->{'Orientation'};
296 $exif->{'ExposureTime'} = $info->{'ExposureTime'};
297 $exif->{'FNumber'} = $info->{'FNumber'};
298 $exif->{'ISO'} = $info->{'ISO'};
299 $exif->{'CreateDate'} = $info->{'CreateDate'};
300 $exif->{'ExposureCompensation'} = $info->{'ExposureCompensation'};
301 $exif->{'Flash'} = $info->{'Flash'};
302 $exif->{'FocalLength'} = $info->{'FocalLength'};
303 #$exif->{'ColorSpace'} = $info->{'ColorSpace'};
304 #$exif->{'FileSource'} = $info->{'FileSource'};
305 $exif->{'ExposureMode'} = $info->{'ExposureMode'};
306 $exif->{'Macro'} = $info->{'Macro'};
307 $exif->{'LensType'} = $info->{'LensType'};
308 return $exif;
309 }
310
311 sub get_exifblock
312 {
313 my $exif = shift;
314 my $exifTool = new Image::ExifTool;
315 my $block = "<table id='exifdata' cellspacing='0'>\n";
316 $block .= "<tr class='exifhead'><td>EXIF Parameter</td><td>Value</td></tr>\n";
317 my $tagcount = 0;
318 my $flipflop = 0;
319 foreach my $tag ( keys %$exif )
320 {
321 next if $tag =~ m/Orientation/;
322 my $val = $exif->{$tag};
323 next unless $val;
324 $block .= "<tr class='exiflight'>" if $flipflop;
325 $block .= "<tr class='exifdark'>" if not $flipflop;
326 $block .= "\t<td>";
327 $block .= $exifTool->GetDescription($tag);
328 $block .= "</td>\n";
329 $block .= "\t<td>";
330 $block .= $val;
331 $block .= "</td>\n";
332 $block .= "</tr>\n";
333 $flipflop = not $flipflop;
334 $tagcount++;
335 }
336 $block .= "</table>";
337 $block = undef if not $tagcount;
338 return $block;
339 }
340
341 sub gen_navscript
342 {
343 my $prev = shift;
344 my $next = shift;
345 my $index = shift;
346
347 $prev =~ s/\.html//;
348 $next =~ s/\.html//;
349
350 my $scriptbuffer = "<script type='text/javascript'>\n";
351 $scriptbuffer .= "\tnav_reg_prev('" . $prev . "');\n" if $prev;
352 $scriptbuffer .= "\tnav_reg_next('" . $next . "');\n" if $next;
353 $scriptbuffer .= "\tnav_reg_index('" . $index . "');\n" if $index;
354 $scriptbuffer .= "\tnav_reg_onkeypress();\n";
355 $scriptbuffer .= "</script>\n";
356 return $scriptbuffer;
357 }
358
359 sub get_config
360 {
361 # My standard way of implementing AppConfig use ...
362 my $filename = shift;
363 if ( not $filename )
364 {
365 my @files = ( "./plsgen.cfg", "../plsgen.cfg", "/etc/plsgen.cfg" );
366 foreach my $file ( @files ) {
367 $filename = $file;
368 last if ( -f $filename );
369 }
370 }
371
372 my $cfg = AppConfig->new(
373 {
374 CASE => 1,
375 ERROR => \&cfg_error,
376 GLOBAL => {
377 DEFAULT => "<unset>",
378 ARGCOUNT => AppConfig::ARGCOUNT_ONE
379 },
380 }
381
382 );
383 $cfg->define('full_tpl_file');
384 $cfg->full_tpl_file("/usr/local/share/plsgen/full.tpl");
385 $cfg->define('index_tpl_file');
386 $cfg->index_tpl_file("/usr/local/share/plsgen/index.tpl");
387 $cfg->define('css_file');
388 $cfg->css_file("../style.css");
389 $cfg->define('navigation_script');
390 $cfg->navigation_script("../nav.js");
391 $cfg->define('columns');
392 $cfg->columns(4);
393 $cfg->define('rows');
394 $cfg->rows(3);
395 $cfg->define('thumb_pre');
396 $cfg->thumb_pre("<div class='thumb'>");
397 $cfg->define('thumb_post');
398 $cfg->thumb_post("</div>");
399 $cfg->define('comment_pre');
400 $cfg->comment_pre("<div id='comment'>");
401 $cfg->define('comment_post');
402 $cfg->thumb_post("</div>");
403 $cfg->define('idx_prev_text');
404 $cfg->idx_prev_text("&larr; Back");
405 $cfg->define('idx_next_text');
406 $cfg->idx_next_text("Next &rarr;");
407 $cfg->define('idx_ret_text');
408 $cfg->idx_ret_text("To index");
409 $cfg->define('footer_tag');
410 $cfg->footer_tag('plsgen : Perl Simple Gallery Generator 0.1');
411
412 $cfg->file($filename) if -f $filename;
413 return $cfg;
414 }
415
416 sub cfg_error {
417 die "Error occured in config-handling: $_\n";
418 }
419