X-Git-Url: http://www.average.org/gitweb/?p=mkgallery.git;a=blobdiff_plain;f=mkgallery.pl;h=a0cb8eebef6089317ae33056ddfef89ca91737e7;hp=53b9c9ad809ae91fb57afe18164b2403b4b39668;hb=c1251ffd78703170ddd2fd6e2d14959f64679da0;hpb=95901ea203be37209c5ac2cadc685826a109026d diff --git a/mkgallery.pl b/mkgallery.pl index 53b9c9a..a0cb8ee 100755 --- a/mkgallery.pl +++ b/mkgallery.pl @@ -28,8 +28,8 @@ package FsObj; use strict; use Carp; -use POSIX qw/getcwd/; -use CGI qw/:html *table *center *div/; +use POSIX qw/getcwd strftime/; +use CGI qw/:html *table *Tr *center *div/; use Image::Info qw/image_info dim/; use Term::ReadLine; use Getopt::Long; @@ -108,6 +108,7 @@ sub iterate { my $fullpath .= $self->{-fullpath}; print "iterate in dir $fullpath\n" if ($debug); + my $youngest=0; my @rdirlist; my @rimglist; my $D; @@ -121,6 +122,8 @@ sub iterate { if ($child->isdir) { push(@rdirlist,$child); } elsif ($child->isimg) { + my @stat = stat($child->{-fullpath}); + $youngest = $stat[9] if ($youngest < $stat[9]); push(@rimglist,$child); } } @@ -129,6 +132,7 @@ sub iterate { undef @rdirlist; # inplace sorting would be handy here my @imglist = sort {$a->{-base} cmp $b->{-base}} @rimglist; undef @rimglist; # optimize away unsorted versions + $self->{-firstimg} = $imglist[0]; print "Dir: $self->{-fullpath}\n" if ($debug); @@ -143,26 +147,42 @@ sub iterate { $dir->iterate; } +# no need to go beyond this point if the directory timestamp did not +# change since we built index.html file last time. + + my @istat = stat($self->{-fullpath}.'/index.html'); + return unless ($youngest > $istat[9]); + # 3. iterate through images to build cross-links, -# create scaled versions and aux htmls my $previmg = undef; foreach my $img(@imglist) { + # list-linking must be done before generating + # aux html because aux pages rely on prev/next refs if ($previmg) { $previmg->{-nextimg} = $img; $img->{-previmg} = $previmg; } $previmg=$img; + } +# 4. create scaled versions and aux html pages + + foreach my $img(@imglist) { + # scaled versions must be generated before aux html + # and main image index because they both rely on + # refs to scaled images and they may be just original + # images, this is not known before we try scaling. $img->makescaled; + # finally, make aux html pages $img->makeaux; } -# 4. start building index.html for the directory +# 5. start building index.html for the directory $self->startindex; -# 5. iterate through subdirectories to build subalbums list +# 6. iterate through subdirectories to build subalbums list if (@dirlist) { $self->startsublist; @@ -172,7 +192,7 @@ sub iterate { $self->endsublist; } -# 6. iterate through images to build thumb list +# 7. iterate through images to build thumb list if (@imglist) { $self->startimglist; @@ -183,7 +203,7 @@ sub iterate { $self->endimglist; } -# 7. comlplete building index.html for the directory +# 8. comlplete building index.html for the directory $self->endindex; } @@ -205,11 +225,49 @@ sub isimg { } return 0; } + + tryapp12($info) unless ($info->{'ExifVersion'}); + $self->{-isimg} = 1; $self->{-info} = $info; return 1; } +sub tryapp12 { + my $info = shift; # this is not a method + my $app12; + # dirty hack to take care of Image::Info parser strangeness + foreach my $k(keys %$info) { + $app12=substr($k,6).$info->{$k} if ($k =~ /^App12-/); + } + return unless ($app12); # bad luck + my $seenfirstline=0; + foreach my $ln(split /[\r\n]+/,$app12) { + $ln =~ s/[[:^print:]\000]/ /g; + unless ($seenfirstline) { + $seenfirstline=1; + $info->{'Make'}=$ln; + next; + } + my ($k,$v)=split /=/,$ln,2; + if ($k eq 'TimeDate') { + $info->{'DateTime'} = + strftime("%Y:%m:%d %H:%M:%S", localtime($v)) + unless ($v < 0); + } elsif ($k eq 'Shutter') { + $info->{'ExposureTime'} = '1/'.int(1000000/$v+.5); + } elsif ($k eq 'Flash') { + $info->{'Flash'} = $v?'Flash fired':'Flash did not fire'; + } elsif ($k eq 'Type') { + $info->{'Model'} = $v; + } elsif ($k eq 'Version') { + $info->{'Software'} = $v; + } elsif ($k eq 'Fnumber') { + $info->{'FNumber'} = $v; + } + } +} + sub initdir { my $self = shift; my $fullpath = $self->{-fullpath}; @@ -258,21 +316,148 @@ sub makescaled { my $max = ($w > $h)?$w:$h; foreach my $size(@sizes) { - my $nfn = $dn.'/.'.$size.'/'.$name; + my $nref = '.'.$size.'/'.$name; + my $nfn = $dn.'/'.$nref; my $factor=$size/$max; + if ($factor >= 1) { + $self->{$size} = $name; # unscaled version will do + } else { + $self->{$size} = $nref; + if (isnewer($fn,$nfn)) { + doscaling($fn,$nfn,$factor,$w,$h); + } + } } } +sub isnewer { + my ($fn1,$fn2) = @_; # this is not a method + my @stat1=stat($fn1); + my @stat2=stat($fn2); + return (!@stat2 || ($stat1[9] > $stat2[9])); + # true if $fn2 is absent or is older than $fn1 +} + +sub doscaling { + my ($src,$dest,$factor,$w,$h) = @_; # this is not a method + my $im = new Image::Magick; + my $err; + print "doscaling $src -> $dest by $factor\n" if ($debug); + $err = $im->Read($src); + unless ($err) { + $im->Scale(width=>$w*$factor,height=>$h*$factor); + $err=$im->Write($dest); + warn "ImageMagick: write \"$dest\": $err" if ($err); + } else { # fallback to command-line tools + warn "ImageMagick: read \"$src\": $err"; + system("djpeg \"$src\" | pnmscale \"$factor\" | cjpeg >\"$dest\""); + } + undef $im; +} + sub makeaux { my $self = shift; - my $fn = $self->{-fullpath}; my $name = $self->{-base}; my $dn = $self->{-parent}->{-fullpath}; + my $pref = $self->{-previmg}->{-base}; + my $nref = $self->{-nextimg}->{-base}; + my $inc = $self->{-inc}; + my $title = $self->{-info}->{'Comment'}; + $title = $name unless ($title); + + print "slide: \"$pref\"->\"$name\"->\"$nref\"\n" if ($debug); + + # slideshow + for my $refresh('static', 'slide') { + my $fn = sprintf("%s/.html/%s-%s.html",$dn,$name,$refresh); + if (isnewer($self->{-fullpath},$fn)) { + my $imgsrc = '../'.$self->{$sizes[1]}; + my $fwdref; + my $bakref; + if ($nref) { + $fwdref = sprintf("%s-%s.html",$nref,$refresh); + } else { + $fwdref = '../index.html'; + } + if ($pref) { + $bakref = sprintf("%s-%s.html",$pref,$refresh); + } else { + $bakref = '../index.html'; + } + my $toggleref; + my $toggletext; + if ($refresh eq 'slide') { + $toggleref=sprintf("%s-static.html",$name); + $toggletext = 'Stop!'; + } else { + $toggleref=sprintf("%s-slide.html",$name); + $toggletext = 'Play->'; + } + my $F; + unless (open($F,'>'.$fn)) { + warn "cannot open \"$fn\": $!"; + next; + } + if ($refresh eq 'slide') { + print $F start_html( + -title=>$title, + -bgcolor=>"#808080", + -head=>meta({-http_equiv=>'Refresh', + -content=>"3; url=$fwdref"}), + -style=>{-src=>$inc."gallery.css"}, + ),"\n"; + + } else { + print $F start_html(-title=>$title, + -bgcolor=>"#808080", + -style=>{-src=>$inc."gallery.css"}, + ),"\n"; + } + print $F start_center,"\n", + h1($title),"\n", + start_table({-class=>'navi'}),start_Tr,"\n", + td(a({-href=>"../index.html"},"Index")),"\n", + td(a({-href=>$bakref},"<<Prev")),"\n", + td(a({-href=>$toggleref},$toggletext)),"\n", + td(a({-href=>$fwdref},"Next>>")),"\n", + end_Tr, + end_table,"\n", + table({-class=>'picframe'}, + Tr(td(img({-src=>$imgsrc})))),"\n", + end_center,"\n", + end_html,"\n"; + close($F); + } + } + + # info html + my $fn = sprintf("%s/.html/%s-info.html",$dn,$name); + if (isnewer($self->{-fullpath},$fn)) { + my $F; + unless (open($F,'>'.$fn)) { + warn "cannot open \"$fn\": $!"; + return; + } + my $imgsrc = sprintf("../.%s/%s",$sizes[0],$name); + print $F start_html(-title=>$title, + -style=>{-src=>$inc."gallery.css"},),"\n", + start_center,"\n", + h1($title),"\n", + table({-class=>'ipage'}, + Tr(td(img({-src=>$imgsrc})), + td($self->infotable))), + a({-href=>'../index.html'},'Index'),"\n", + end_center,"\n", + end_html,"\n"; + close($F); + } } sub startindex { my $self = shift; my $fn = $self->{-fullpath}.'/index.html'; + my $block = $self->{-fullpath}.'/.noindex'; + $fn = '/dev/null' if ( -f $block ); my $IND; unless (open($IND,'>'.$fn)) { warn "cannot open $fn: $!"; @@ -331,16 +516,45 @@ sub endsublist { sub startimglist { my $self = shift; my $IND = $self->{-IND}; + my $first = $self->{-firstimg}->{-base}; + my $slideref = sprintf(".html/%s-slide.html",$first); - print $IND h2("Images"),"\n"; + print $IND h2("Images"),"\n", + a({-href=>$slideref},'Slideshow'), + "\n"; } sub img_entry { my $self = shift; my $IND = $self->{-parent}->{-IND}; my $name = $self->{-base}; - - print $IND a({-href=>$name},$name),"\n"; + my $title = $self->{-info}->{'Comment'}; + $title = $name unless ($title); + my $thumb = $self->{$sizes[0]}; + my $medium = $self->{$sizes[1]}; + my $info = $self->{-info}; + my ($w, $h) = dim($info); + + print $IND start_div({-class=>'ibox',-id=>$name, + -OnClick=>"HideIbox('$name');"}),"\n", + start_div({-class=>'iboxtitle'}), + span({-style=>'float: left;'},b("Info for $name")), + span({-style=>'float: right;'}, + a({-href=>"#",-OnClick=>"HideIbox('$name');"},"Close")), + br({-clear=>'all'}),"\n", + end_div,"\n", + $self->infotable, + end_div,"\n"; + + print $IND table({-class=>'slide'},Tr(td( + a({-href=>".html/$name-info.html",-title=>'Image Info', + -onClick=>"return showIbox('$name');"},$title), + br, + a({-href=>$medium,-rel=>"lightbox",-title=>$title}, + img({-src=>$thumb})), + br, + a({-href=>$name,-title=>'Original Image'},"($w x $h)"), + br))),"\n"; } sub endimglist { @@ -350,224 +564,10 @@ sub endimglist { print $IND br({-clear=>'all'}),hr,"\n\n"; } -###################################################################### -=cut -###################################################################### - -&processdir(getcwd); - -sub processdir { - my ($start,$dir)=@_; - my $dn=$start; - $dn .= "/".$dir if ($dir); - unless ( -d $dn ) { - warn "not a directory: $dn"; - return; - } - my $D; - unless (opendir($D,$dn)) { - warn "cannot opendir $dn: $!"; - return; - } - -# recurse into subdirectories BEFORE opening index file - - &iteratedir($D,$start,$dir,sub { - my ($start,$dir,$base)=@_; - my $ndir = $dir; - $ndir .= "/" if ($ndir); - $ndir .= $base; - return unless ( -d $start."/".$ndir ); - &processdir($start,$ndir); - }); - -# fill in title - - my $title=&gettitle($dn,$dir); - -# get include prefix - - my $inc=&getinclude($dn); - -# generate directory index unless suppressed - - if ( -e $dn."/.noindex" ) { - open(STDOUT,">/dev/null"); - } else { - open(STDOUT,">".$dn."/index.html"); - } - -# write HTML header - - -style=>{-src=>[$inc."gallery.css", - $inc."lightbox.css"]}, - -script=>[{-code=>"var incPrefix='$inc';"}, - {-src=>$inc."gallery.js"}, - {-src=>$inc."lightbox.js"}]),"\n"; - print a({-href=>"../index.html"},"UP"); - print start_center,"\n"; - print h1($title),"\n"; - -# create list of sub-albums - - my $hassubdirs=0; - &iteratedir($D,$start,$dir,sub { - my ($start,$dir,$base)=@_; - my $en=sprintf("%s/%s/%s",$start,$dir,$base); - return unless ( -d $en ); - unless ($hassubdirs) { - print hr,h2("Albums"),start_table,"\n"; - $hassubdirs=1; - } - &subalbum($base,&gettitle($en,$dir."/".$base)); - }); - print end_table,hr,"\n" if ($hassubdirs); - -# create picture gallery - - my @piclist=(); - my @infolist=(); - - my $haspics=0; - &iteratedir($D,$start,$dir,sub { - my ($start,$dir,$base)=@_; - my $en=sprintf("%s/%s/%s",$start,$dir,$base); - return unless ( -f $en ); - my $info = image_info($en); - if (my $error = $info->{error}) { - if (($error !~ "Unrecognized file format") && - ($error !~ "Can't read head")) { - print STDERR "File \"$en\": $error\n"; - } - return; - } - if (&processfile($start,$dir,$base,$en,$info)) { - $haspics=1; - push(@piclist,$base); - push(@infolist,$info); - } - }); - -# write HTML footer - - print br({-clear=>"all"}),"\n"; - print a({-href=>".html/".$piclist[0]."-slide.html"},"Slideshow"); - print hr,"\n" if ($haspics); - print end_center,"\n"; - print end_html,"\n"; - - close(STDOUT); - closedir($D); - -# generate html files for slideshow from @piclist - - for (my $i=0;$i<=$#piclist;$i++) { - my $base=$piclist[$i]; - my $pbase; - my $nbase; - $pbase=$piclist[$i-1] if ($i>0); - $nbase=$piclist[$i+1] if ($i<$#piclist); - for my $refresh('static','slide') { - &mkauxfile($start,$dir,$pbase,$base,$nbase, - $refresh,$infolist[$i]); - } - } - -} - -############################################################# -# helper functions -############################################################# - -sub iteratedir { - my ($D,$start,$dir,$prog)=@_; - my @list=(); - while (my $de=readdir($D)) { - next if ($de =~ /^\./); - push(@list,$de); - } - foreach my $de(sort @list) { - &$prog($start,$dir,$de); - } - rewinddir($D); -} - -sub getinclude { - my ($dn)=@_; - - my $depth=20; - my $str=""; - #print STDERR "start include ",$dn."/".$str.".include","\n"; - while ( ! -d $dn."/".$str.".include" ) { - #print STDERR "not include ",$dn."/".$str.".include","\n"; - $str.="../"; - last unless ($depth--); - } - #print STDERR "end include ",$dn."/".$str.".include","\n"; - if ( -d $dn."/".$str.".include" ) { - #print STDERR "return include ".$str.".include/".$fn,"\n"; - return $str.".include/"; - } else { - return ""; # won't work anyway but return something - } -} - -sub gettitle { - my ($dir,$dflt)=@_; - - my $F; - my $str; - if (open($F,"<".$dir."/.title")) { - $str=<$F>; - chop $str; - close($F); - } else { - print STDERR "enter title for $dir\n"; - $str=<>; - if ($str =~ /^\s*$/) { - $str=$dflt; - } - if (open($F,">".$dir."/.title")) { - print $F $str,"\n"; - close($F); - } else { - print STDERR "cant open .title in $dir for writing: $!"; - } - } - return $str; -} - -sub subalbum { - my ($base,$title)=@_; - - print Tr({-bgcolor=>"#c0c0c0"}, - td(a({-href=>$base."/index.html"},$base)), - td(a({-href=>$base."/index.html"},$title))),"\n"; -} - -sub processfile { - my ($start,$dir,$base,$fn,$info)=@_; - - my ($w,$h) = dim($info); - my $title=$info->{'Comment'}; - $title=$base unless ($title); - my $thumb=&scale($start,$dir,$base,$fn,160,$info); - my $medium=&scale($start,$dir,$base,$fn,640,$info); - print &infobox($info,$base,$fn),"\n"; - print table({-class=>'slide'},Tr(td( - a({-href=>".html/$base-info.html", - -onClick=>"return showIbox('$base');"},$title), - br, - a({-href=>$medium,-rel=>"lightbox",-title=>$title}, - img({-src=>$thumb})), - br, - a({-href=>$base},"($w x $h)"), - br))),"\n"; - return 1; -} - -sub infobox { - my ($info,$base,$fn)=@_; +sub infotable { + my $self = shift; + my $info = $self->{-info}; + my $msg=''; my @infokeys=( 'DateTime', @@ -583,112 +583,10 @@ sub infobox { 'Model', 'Software', ); - - my $msg=start_div({-class=>'ibox',-id=>$base,-OnClick=>"HideIbox('$base');"}); - $msg.=span({-style=>'float: left;'},"Info for $base"). - span({-style=>'float: right;'}, - a({-href=>"#",-OnClick=>"HideIbox('$base');"},"Close")); - $msg.=br({-clear=>'all'}); - $msg.=start_table; + $msg.=start_table({-class=>'infotable'})."\n"; foreach my $k(@infokeys) { - $msg.=Tr(td($k.":"),td($info->{$k})); - } - $msg.=end_table; - $msg.=end_div; - return $msg; -} - -sub mkauxfile { - my ($start,$dir,$pbase,$base,$nbase,$refresh,$info) =@_; - my $en=sprintf("%s/%s/.html/%s-%s.html",$start,$dir,$base,$refresh); - my $pref; - my $nref; - if ($pbase) { - $pref=sprintf("%s-%s.html",$pbase,$refresh); - } else { - $pref="../index.html"; - } - if ($nbase) { - $nref=sprintf("%s-%s.html",$nbase,$refresh); - } else { - $nref="../index.html"; - } - my $toggle; - my $toggleref; - if ($refresh eq 'slide') { - $toggle='Stop!'; - $toggleref=sprintf("%s-static.html",$base); - } else { - $toggle='Play->'; - $toggleref=sprintf("%s-slide.html",$base); + $msg.=Tr(td($k.":"),td($info->{$k}))."\n" if ($info->{$k}); } - - my $tdir=sprintf "%s/%s/.html",$start,$dir; - mkdir($tdir,0755) unless ( -d $tdir ); - - unless (open(STDOUT,">".$en)) { - warn "cannot open $en: $!"; - return; - } - my $title=$info->{'Comment'}; - $title=$base unless ($title); - if ($refresh eq 'slide') { - print start_html(-title=>$title, - -bgcolor=>"#808080", - -head=>meta({-http_equiv=>'Refresh', - -content=>"3; url=$nref"})),"\n"; - } else { - print start_html(-title=>$title, - -bgcolor=>"#808080"),"\n"; - } - print start_center,"\n"; - print h1($title); - print a({-href=>"../index.html"},"Index")," | "; - print a({-href=>$pref},"<<Prev")," | "; - print a({-href=>$toggleref},$toggle)," | "; - print a({-href=>$nref},"Next>>"); - print p; - print img({-src=>"../.640/".$base}),"\n"; - print end_center,"\n"; - print end_html,"\n"; - close(STDOUT); -} - -sub scale { - my ($start,$dir,$base,$fn,$tsize,$info)=@_; - my ($w,$h) = dim($info); - my $max=($w>$h)?$w:$h; - my $factor=$tsize/$max; - - return $base if ($factor >= 1); - - my $tdir=sprintf "%s/%s/.%s",$start,$dir,$tsize; - mkdir($tdir,0755) unless ( -d $tdir ); - my $tbase=sprintf ".%s/%s",$tsize,$base; - my $tfn=sprintf "%s/%s",$tdir,$base; - my @sstat=stat($fn); - my @tstat=stat($tfn); - return $tbase if (@tstat && ($sstat[9] < $tstat[9])); # [9] -> mtime - - print STDERR "scale by $factor from $fn to $tfn\n"; - &doscaling($fn,$tfn,$factor,$w,$h); - return $tbase; + $msg.=end_table."\n"; } -sub doscaling { - my ($src,$dest,$factor,$w,$h)=@_; - - my $im=new Image::Magick; - my $err; - #print STDERR "doscale $src -> $dest by $factor\n"; - $err=$im->Read($src); - unless ($err) { - $im->Scale(width=>$w*$factor,height=>$h*$factor); - $err=$im->Write($dest); - warn "ImageMagic: write \"$dest\": $err" if ($err); - } else { - warn "ImageMagic: read \"$src\": $err"; - system("djpeg \"$src\" | pnmscale \"$factor\" | cjpeg >\"$dest\""); - } - undef $im; -}