#!/usr/bin/perl # make photo index, including searchable captions and # Google map links # Andrew Daviel 1998-2006 advax@triumf.ca # reads captions from a file "captions.txt" # reads position information from BTREE database # reads EXIF data from my camera, empirically # (only want timestamp, anyway) # philosophy: makes clickable photo index page which can # be resized as desired and scrolled in just one direction # (as opposed to a fixed-width table) # full-size photos open in another (named) window, as do Google maps. # caption, position and timestamp data are cached in a local DB # for faster indexing # currently all the photos are re-indexed every time, but it only takes # a minute or so for a thousand photos. # also, the photos are assumed to be all in the current direcctory use Time::Local ; use DB_File ; $maxph = 150 ; # maximum thumbnails per page; reduce for # low bandwidth or popular sites sub compare { my ($k1,$k2) = @_ ; $k1 <=> $k2 ; } $DB_BTREE->{'compare'} = \&compare ; $trackh = tie %track, "DB_File", "trackdb", O_RDWR, 0666, $DB_BTREE or die "Cannot open file trackdb: $!\n" ; $key = shift ; if ($key) { $value = 'undef'; $orig_key = $key ; $trackh->seq($key, $value, R_CURSOR) ; print "next $orig_key\t-> $key\t-> $value\n" ; $key = $orig_key ; $value = 'undef'; $trackh->seq($key, $value, R_PREV) ; print "prev $orig_key\t-> $key\t-> $value\n" ; exit ; } $now = localtime() ; $dir = &escape($ENV{PWD}) ; $verbose = $ARGV[0] ; $movies = 1 ; $addcap = 1 ; dbmopen(%taken,"taken.db",0666) ; dbmopen(%caption,"captions.db",0666) ; dbmopen(%size,"size.db",0666) ; system("chmod 666 captions.db") ; open (IN,"captions.txt") ; open (CAP,">captions.bak") ; while () { print CAP $_ ; chomp ; if (/^#/) { next ; } ($img,$caption) = split(/\t/) ; unless ($caption{$img}) { $caption{$img} = $caption ; } if ($img eq 'ALBUM') { $album = $caption{$img} ; } } close(IN) ; close(CAP) ; $nph = $np = 0 ; opendir(DIR,".") or die ; foreach $_ (readdir(DIR)) { unless (/(.*)\.JPG/i or /(.*)\.PNG/i or /(.*)\.MOV/ or /(.*)\.jpeg/i) { next ; } if (/_tn.jpg/) { next ; } if (/^thumb_/) { next ; } $nph++ ; if ($nph > $maxph) { $np++ ; $nph = 0 ; } } $nph = 0 ; $page = $page0 = '' ; $page1 = 1 ; open (CAP,">captions.txt") ; opendir(DIR,".") or die ; open (OUT,">index$page.html") or die ; open (OUT2,">pindex$page.html") or die ; $page = $page0 = '' ; $page1 = 1 ; &do_head ; $dtime00 = 0 ; foreach $_ (sort { $a cmp $b } readdir(DIR)) { #unless (/([\d]+_[\d]+).JPG/) { next ; } unless (/(.*)\.JPG/i or /(.*)\.PNG/i or /(.*)\.MOV/ or /(.*)\.jpeg/i) { next ; } if (/(.*)\.MOV/) { $ismovie = "Movie: " ; $alt = "Movie" ; } else { $ismovie = '' ; $alt = "Photo" ; } if (/_tn.jpg/) { next ; } if (/^thumb_/) { next ; } #$th = "thumb_$1.jpg" ; $ph = $_ ; $id = $1 ; $th = "$1_tn.jpg" ; $ph = $_ ; $id = $1 ; unless ($ismovie) { if ($size{$ph} =~ /([\d]+)x([\d]+)/) { $w1 = $1 ; $h1 = $2 ; } elsif (`identify \"$ph\"` =~ /([\d]+)x([\d]+)/) { $w1 = $1 ; $h1 = $2 ; $size{$ph} = $w1.'x'.$h1 ; } else { print STDERR "Can't get size for $ph\n" ; $w1 = $h1 = '?' ; } if ($h1 > $w1 ) { $h = 160 ; $w = int(160*$w1/$h1) ; } else { $w = 160 ; $h = int(160*$h1/$w1) ; } } @stat = stat($ph) ; @date = localtime($stat[9]) ; $byte = int($stat[7]/1024).'kb' ; $year = $date[5]+1900 ; $mon = $date[4]+1 ; $day = $date[3] ; $date = "$year-$mon-$day" ; $size1 = $w1.'x'.$h1 ; $size = $w.'x'.$h ; $taken = $time2 = '' ; if ($taken{$ph} =~ /[\d]{4}:[\d]{2}:[\d]{2} /) { $taken = $taken{$ph} ; } else { open (IN,$ph) ; sysseek (IN,820,0) ; # Kodak embedded date $s = sysread (IN,$taken,19) ; close (IN) ; } $maplink = '' ; unless ($taken =~ /[\d]{4}:[\d]{2}:[\d]{2} /) { $taken = '' ; } else { #print STDERR "$ph taken $taken\n" ; # 2004:02:04 09:21:24 if ($taken =~ /([\d]+):([\d]+):([\d]+) ([\d]+):([\d]+):([\d]+)/) { $taken{$ph} = $taken ; $year = $1 ; $mon = $2 ; $mon-- ; $year -= 1900 ; $time2 = timelocal($6,$5,$4,$3,$mon,$year) ; $dtime0 = $time2 - $time20 ; print "$ph dtime $dtime0 taken $taken\n" ; if ($dtime0 < 1) { $slip = abs($dtime0) ; print "$ph time slip\n" ; } if ($slip and ($dtime0 > $slip) ) { print "$ph time unslip\n" ; $slip = 0 ; } $key2 = $time2 ; $next = '' ; $trackh->seq($key2, $next, R_CURSOR) ; $key1 = $time2 ; $prev = ''; $trackh->seq($key1, $prev, R_PREV) ; if ($next and $prev) { $dtime = $key2 - $key1 ; $dtime2 = $key2 - $time2 ; $dtime1 = $time2 - $key1 ; $taken = localtime($time2) ; $date1 = localtime($key1) ; $date2 = localtime($key2) ; ($lat1,$long1) = split(/,/,$prev) ; ($lat2,$long2) = split(/,/,$next) ; if ($dtime < 400) { $lat = $lat1 + ($lat2-$lat1) * $dtime1/$dtime ; $long = $long1 + ($long2-$long1) * $dtime1/$dtime ; } elsif ($dtime1 < 500) { $long = $long1 ; $lat = $lat1 ; } elsif ($dtime2 < 500) { $long = $long2 ; $lat = $lat2 ; } else { $long = $lat = '' ; } print "$ph taken $taken, dtime $dtime\n" ; print "prev $date1\t-> $prev $dtime1\n" ; print "next $date2\t-> $next $dtime2\n" ; if ($lat and ($slip == 0)) { $pos = sprintf("%.6f,%.6f",$lat,$long) ; $pos2 = sprintf("%.6f,%.6f",$long,$lat) ; print "guess $pos\n\n" ; $maplink = "\"Map\"" ; $maplink = "\"Map\"" ; # t=k sat #t=h hybrid else map } else { print "unguessable\n\n" ; } } } $time20 = $time2 ; } if ($verbose) { print STDERR "$ph $date $taken $size1 $byte" ; } if ($taken and ($slip == 0 )) { $date = $taken ; } if ( -s $th) { if ($verbose) { print STDERR " - $th exists\n" ; } if (`identify \"$th\"` =~ /([\d]+)x([\d]+)/) { $wt = $1 ; $ht = $2 ; if ($h1 > $w1 and $ht < $wt) { $cmd = "convert -size $size \"$ph\" -resize $size \"$th\"" ; $cmd2 = "touch -r \"$ph\" \"$th\"" ; if ($verbose) { print STDERR " - remake $th size $size\n" ; } system($cmd) ; system($cmd2) ; } } } else { if ($ismovie) { $cmd1 = "mplayer -nosound -frames 1 -vo pnm \"$ph\" >/dev/null" ; $cmd2 = "convert -size $size 00000001.ppm -resize $size \"$th\"" ; if ($verbose) { print STDERR " - make $th size $size from movie\n" ; } system($cmd1) ; system($cmd2) ; $size1 = '' ; unlink('00000001.jpg') ; unlink('00000002.jpg') ; } else { $cmd = "convert -size $size \"$ph\" -resize $size \"$th\"" ; $cmd2 = "touch -r \"$ph\" \"$th\"" ; if ($verbose) { print STDERR " - make $th size $size\n" ; } #if ($verbose) { print STDERR "$cmd - width=$w height=$h\n" ; } system($cmd) ; system($cmd2) ; } } print OUT< $alt $caption{$ph} EOT print OUT2< $alt $caption{$ph} EOT if ($addcap) { $ecap = &escape($caption) ; $eph = &escape($ph) ; #print OUT "Add Caption\n" ; print OUT "$maplink\n" ; print OUT2 "\"Add$maplink\n" ; # print OUT "
" ; # print OUT "" ; # print OUT "" ; # print OUT "" ; # print OUT "" ; # print OUT "
\n" ; } print OUT "\n" ; print OUT2 "\n" ; print CAP "$ph\t$caption{$ph}\n" ; $nph++ ; if ($nph > $maxph) { &do_tail ; $page0 = $page ; $page = $page1 ; $page1++ ; $nph = 0 ; open (OUT,">index$page.html") or die ; open (OUT2,">pindex$page.html") or die ; &do_head ; } } if ($movies) { print OUT<

Movies:
Quicktime format from Kodak CX6230 camera. Video is 320 x 240 motion JPEG; audio is 11025 samples/sec 8 bit mono. Possible players: Apple Quicktime (Mac, Windows), mplayer, Xine (Linux). Mplayer needs to register QuickTime.qts for 0x454E4F4E audio. EOT print OUT2<

Movies:
Quicktime format from Kodak CX6230 camera. Video is 320 x 240 motion JPEG; audio is 11025 samples/sec 8 bit mono. Possible players: Apple Quicktime (Mac, Windows), mplayer, Xine (Linux). Mplayer needs to register QuickTime.qts for 0x454E4F4E audio. EOT } &do_tail ; exit ; sub do_tail { print OUT "
\n


\n" ; print OUT2 "
\n
\n" ; if ($page) { print OUT "Previous\n" ; print OUT2 "Previous\n" ; } if ($page < $np) { print OUT "Next\n" ; print OUT2 "Next\n" ; } print OUT "\n\n" ; print OUT2 "\n\n" ; } sub escape { my $str = @_[0] ; my $pat = '[\x00-\x29+,/:;<=>?\x5B-\x5E`\x7B-\xFF]' ; $str =~ s/($pat)/sprintf("%%%02lx",unpack('C',$1))/ge; return($str); } sub do_head { my $title = 'Photo Index' ; if ($album) { $title = $album ; } print OUT<$title

$title

Index generated $now - This window may be resized as desired

Hold mouse over thumbnail for date/size; click for fullsize (opens in second window).


EOT print OUT2<$title

$title

Index generated $now - This window may be resized as desired

Hold mouse over thumbnail for date/size; click for fullsize (opens in second window). Note that some images link to short Quicktime movies.


EOT if ($page) { print OUT "Page $page\n

\n" ; print OUT2 "Page $page\n

\n" ; print OUT "Previous\n" ; print OUT2 "Previous\n" ; } if ($page < $np) { print OUT "Next\n" ; print OUT2 "Next\n" ; } for ($p=0;$p<=$np;$p++) { my $p2 = $p ; unless ($p) { $p2 = '' ; } print OUT " $p" ; print OUT2 " $p" ; } print OUT "

\n" ; print OUT2 "

\n" ; }