#!/usr/bin/perl -w
#
# captivate: read photo metadata from jpeg files using exiftool or epinfo,
# display the images and prompt the user for captions, then write
# the combined metadata to RDF and HTML files for each image.
#
# Gerald Oskoboiny, 4 Sep 2000
# contribution Matthieu Fuzellier, since Jan 2006
#
# original source: http://impressive.net/software/photo/source/captivate
# modified version source: http://www.mattiouz.com/software/photo/source/captivate
#
# license: GPL
#
# $Id: captivate.pl 143 2008-04-08 20:23:54Z matthieu $
use strict;
use lib "/home/matthieu/bin/Exiftool/lib";
use Image::ExifTool;
my $debuggin = 0;
my $exiftool = 'exiftool';
my $temp = "/tmp/captivate.$$";
my $edit_instructions =
qq{# Enter text for these photos in the space above. Lines in this file starting
# with '#' will be ignored; leading and trailing whitespace will be removed
# from field values. Field values may NOT yet span multiple lines.
};
my $revision = q$Revision: 143 $;
$revision =~ s/Revision: ([\d\.]+) /$1/;
my @field_order = ( "dc:title", "dc:description", "dc:coverage", "dc:date",
"dc:creator", "dc:publisher", "dc:type", "dc:format",
"dc:source", "dc:identifier", "dc:relation",
"exif:Artist", "exif:Copyright", "exif:ImageDescription",
"exif:UserComment", "exif:Make", "exif:Model",
"exif:Orientation", "exif:XResolution", "exif:YResolution",
"exif:ResolutionUnit", "exif:ExposureTime", "exif:FNumber",
"exif:ISOSpeedRatings", "exif:MeteringMode",
"exif:LightSource", "exif:Flash", "exif:FocalLength",
"exif:ExifImageWidth", "exif:ExifImageLength",
# GPS tags info?
"exif:GPSLatitude", "exif:GPSLatitudeRef", "exif:GPSLongitude",
"exif:GPSLongitudeRef", "exif:GPSAltitude", "exif:GPSAltitudeRef",
# @@ anything else interesting in exif info?
# IPTC tags info ?
"iptc:City", "iptc:Province-State", "iptc:Country-PrimaryLocationName",
# Other
"cap:depicts", "cap:visibility", "cap:quality",
"cap:GPSLatitudeNum", "cap:GPSLongitudeNum", "cap:local-time",
"cap:coord", "t:camera" );
my %uri_fields = ( "cap:header" => 1,
"cap:footer" => 1,
"cap:adsense-bottom" => 1,
"cap:adsense-right" => 1,
"dc:publisher" => 1,
"dc:creator" => 1,
"t:camera" => 1 );
my @wanted_fields= ( "dc:title", "dc:description", "dc:coverage",
"cap:depicts", "cap:visibility", "cap:quality" );
our $variant_pat = '-(sq|tn|sm|med)'; # pattern that matches variants
my $defaults_file = ".captivate.rdf";
my $global_defaults = $ENV{HOME} . "/.captivate/" . $defaults_file;
my $already_printed_header = 0;
my $already_printed_footer = 0;
my $already_printed_index_in_htaccess = 0;
my $curr_image_num = 1;
my $curr_vid_num = 1;
my $privateUsers = "matthieu";
my $friendsUsers = "valid-user";
(my $second, my $minute, my $hour, my $dayOfMonth, my $month, my $yearOffset, my $dayOfWeek, my $dayOfYear, my $daylightSavings) = localtime();
my $currentYear = 1900 + $yearOffset;
my $currentMonth = $month + 1;
my $exifTool = new Image::ExifTool;
my %metadata; # global hash to keep track of image metadata
my %collection; # global hash to keep track of data about the collection
my %opt; # global hash to keep track of command line options
# parse command line arguments
my @files = &parse_args( \%opt, \@ARGV );
# create an index file if needed
if ( $opt{index} ) {
my $temp = $opt{indexfile} . ".tmp";
open( INDEX, "> $temp" ) or do {
print STDERR "error creating index file, $temp: $!\n";
print STDERR "Skipping index file generation.\n";
$opt{index} = 0;
}
}
# create a public index file if needed
if ( $opt{index} && $opt{publicIndex} ) {
my $temp = $opt{indexfile}; $temp =~ s/\.html$/.public.html.tmp/;
open( PUBLICINDEX, "> $temp" ) or do {
print STDERR "error creating public index file, $temp: $!\n";
print STDERR "Skipping index file generation.\n";
$opt{publicIndex} = 0;
}
}
# create a .htaccess file if needed
if ( $opt{generateHtaccess} ) {
my $temp = ".htaccess";
open( HTACCESS, "> $temp" ) or do {
print STDERR "error creating .htaccess file, $temp: $!\n";
print STDERR "Skipping htaccess file generation.\n";
$opt{generateHtaccess} = 0;
}
}
&find_metadata_for_file( \%metadata, "___bogus" );
# Read header, footer and ads from text files
&read_header_from_file;
&read_footer_from_file;
&read_adsense_from_file;
# Generate header for index file if needed
&print_index_header if $opt{index};
# remember some stuff for next/prev links on web pages
my $prev_file;
foreach my $file (@files) {
undef %metadata;
&find_metadata_for_file( \%metadata, $file );
if ( defined $prev_file and ($prev_file ne "") ) {
$collection{$file}{prevfile} = $prev_file;
$collection{$prev_file}{nextfile} = $file;
}
$collection{$file}{title} = $metadata{"dc:title"};
my $visibility = "friends";
$visibility = "public" if ($metadata{"exif:UserComment"} =~ m/visibility:public/);
$visibility = "private" if ($metadata{"exif:UserComment"} =~ m/visibility:private/);
$collection{$file}{visibility} = $visibility;
$prev_file = $file;
}
# generate output in misc formats for each file specified
foreach my $file (@files) {
undef %metadata;
# Read metadata for the specified files (from rdf or exiftool)
&find_metadata_for_file( \%metadata, $file );
my $isPublicPhoto = ($metadata{"exif:UserComment"} =~ m/visibility:public/);
&generate_rdf( \%metadata, $file );
$metadata{"infotext"} = &generate_infotext( \%metadata, $file );
&generate_html( \%metadata, $file, $isPublicPhoto );
if ($opt{index}) {
my $indexEntry = &generate_index_entry( \%metadata, $file );
print INDEX $indexEntry;
if ( $opt{publicIndex} && $isPublicPhoto ) {
$indexEntry =~s/\.html/.public.html/g;
print PUBLICINDEX $indexEntry;
}
}
print HTACCESS &generate_htaccess_entry( \%metadata, $file );
}
# generate output for videos
if ( $opt{videos} ) {
# Get a list of all the videos
opendir(VIDEOSDIR, $opt{videosDir});
my @videos = grep(/\.flv$/,readdir(VIDEOSDIR));
closedir(VIDEOSDIR);
my $videosCount = @videos;
if ( $opt{index} and ($videosCount > 0) ) {
my $videosHeader = qq{ \n};
$videosHeader .= qq{\n};
$videosHeader .= qq{
Videos:
\n\n};
print INDEX $videosHeader;
print "Video count: $videosCount\n" if $debuggin;
}
# Loop foreach video
foreach my $video (@videos) {
print "Video: $video\n" if $debuggin;
my $videoDir = $opt{videosDir};
$videoDir =~ s,/$,,; # strip trailing '/' if it exists
my $videoJpg = $videoDir . "/" . $video;
$videoJpg =~ s/\.flv$/.jpg/;
# We NEED a jpg thumbnail for each video. metadata will be extracted from that thumbnail's exif
undef %metadata;
&find_metadata_for_file( \%metadata, $videoJpg );
$metadata{"infotext"} = &generate_infotext( \%metadata, $videoDir."/".$videoJpg );
&generate_rdf( \%metadata, $videoJpg );
# Generate html for videos
&generate_video_html( $video );
# If $opt{index} generate index entry fo video.
if ($opt{index}) {
my $indexVideoEntry = &generate_index_video_entry( $video );
print INDEX $indexVideoEntry;
}
}
}
# Generate footer for index file if needed and close file(s)
if ( $opt{index} ) {
&print_index_footer;
close( INDEX ) or warn "error closing index file: $!";
&replace_file_iff_different( $opt{indexfile}, $opt{indexfile} . ".tmp" );
if ($opt{publicIndex}) {
close( PUBLICINDEX ) or warn "error closing public index file: $!";
my $pubIndex = $opt{indexfile}; $pubIndex =~ s/\.html$/.public.html/;
&replace_file_iff_different( $pubIndex, $pubIndex . ".tmp" );
}
}
# Close .htaccess file
if ( $opt{generateHtaccess} ) {
close( HTACCESS ) or warn "error closing htaccess file: $!";
}
unlink $temp;
exit;
############################### Functions ###################################
### Args ###
sub parse_args {
# parse command line arguments, canonicalize image filenames, etc.
my ($opt_ref,$args_ref) = @_;
my $opt = %$opt_ref; # deref
my @args = @$args_ref; # deref
my %seen;
my @files;
$opt{mapZoom} = 14;
$opt{generateHtaccess} = 1;
while (my $arg = shift @args) {
# @@ should use Getopt::Long instead of parsing args manually
if (( $arg eq "--publicIndex" ) || ( $arg eq "-p" )) {
$opt{publicIndex} = 1;
} elsif (( $arg eq "--satellite" ) || ( $arg eq "-s" )) {
$opt{satellite} = 1;
} elsif (( $arg eq "--videos" ) || ( $arg eq "-V" )) {
$opt{videos} = 1;
$opt{videosDir} = shift @args;
if ( ! length $opt{videosDir} ) {
print STDERR "ignoring --videos; no directory specified!?";
$opt{videos} = 0;
}
} elsif (( $arg eq "--index" ) || ( $arg eq "-i" )) {
$opt{index} = 1;
$opt{indexfile} = shift @args;
if ( ! length $opt{indexfile} ) {
print STDERR "ignoring --index; no filename specified!?";
$opt{index} = 0;
}
} elsif (( $arg eq "--nohtaccess" ) || ( $arg eq "-A" )) {
$opt{generateHtaccess} = 0;
} elsif (( $arg eq "--title" ) || ( $arg eq "-t" )) {
$opt{indextitle} = shift @args;
if ( ! length $opt{indextitle} ) {
print STDERR "ignoring --title; no title specified!?";
}
} elsif (( $arg eq "--mapzoom" ) || ( $arg eq "-z" )) {
$opt{mapZoom} = shift @args;
if ( ! length $opt{mapZoom} ) {
print STDERR "ignoring --mapzoom; no zoom specified!?";
}
} elsif ( $arg =~ /^-/ ) { # unknown option specified?
&print_usage;
} else {
my $basefile = $arg;
$basefile =~ s,\.[^\.]+$,,; # strip file extension
$basefile =~ s,$variant_pat$,,; # strip '-sm'/'-med'/...
my $file = $basefile . ".jpg";
if ( ! -f $file ) {
print "skipping $arg because $file is not a file\n" if $debuggin;
next;
}
if ( $seen{$file} ) {
print "skipping $arg because we have already seen file $file\n"
if $debuggin;
} else {
$seen{$file}++;
push( @files, $file ); # add this file to the list
}
}
}
return @files;
}
sub print_usage {
# @@ write a real usage blurb sometime
print STDERR "\nSYNTAX ERROR! ";
print STDERR "http://www.mnftiu.cc/mnftiu.cc/images/filing.029.gif\n\n";
exit -1;
}
### Metadata ###
sub find_metadata_for_file {
my ($metadata_ref, $file) = @_;
my $metadata = %$metadata_ref; # deref
&read_rdf_from_file( \%metadata, $global_defaults ) && &error_missing_global_defaults;
# @@ inherit defaults from parent directories, too
&read_rdf_from_file( \%metadata, $defaults_file );
my $rdffile = $file;
$rdffile =~ s,\.[^\.]+$,.rdf,; # name of the .rdf file associated with $file
# read in any previously-stored RDF values
&read_rdf_from_file( \%metadata, $rdffile );
# read metadata from image file using exiftool
&read_exiftool_from_file( \%metadata, $file )
unless ( defined $metadata{"dc:date"} and length( $metadata{"dc:date"} ) == 20 );
# already got metadata
}
sub read_rdf_from_file {
my ($metadata_ref, $rdf_file) = @_;
my $metadata = %$metadata_ref; # deref
if ( ! -r $rdf_file ) {
return -1;
}
print "Reading rdf values from $rdf_file...\n" if $debuggin;
open( DEFAULTS, "< $rdf_file" ) or warn "could not read from rdf file $rdf_file because $!";
while () {
# should maybe set $/ = ">" to parse this? (or use a real parser)
# @@ should probably preserve unknown stuff instead of nuking it
chomp;
next if /^\s*<\?xml version/; #skip xml decl
next if /^\s*<\/?rdf/;
next if /^\s*xmlns:/;
# sample:
#
if (my($key,$value)=(/^\s*<([^ >]+)\s+rdf:resource="([^"]*)"\/>\s*$/)){
if ( ! defined $uri_fields{$key} ) {
print STDERR "Error in rdf file $rdf_file:\n";
print STDERR "$key is not a URI-happy field.\n";
print STDERR "I'll just pretend I never saw that...\n\n";
next;
}
$metadata{$key} = $value;
print "r_d: assigned key [$key] value [$value]\n" if $debuggin;
}
# sample:
# Gerald looking really cool
if (my ($key,$value) = (/^\s*<([^ >]+)>([^<]*)<\//)) {
$metadata{$key} = $value;
print "r_d: assigned key [$key] value [$value]\n" if $debuggin;
}
}
close DEFAULTS or warn "error closing rdf file $rdf_file: $!";
return 0;
}
sub error_missing_global_defaults {
print STDERR "You need to create $global_defaults with some default values.\n";
print STDERR "Sample: http://impressive.net/people/gerald/misc/dotfiles/captivate.rdf\n\n";
print STDERR "At a minimum, cap:viewer and cap:editor need to be defined.\n\n";
exit;
}
sub read_exiftool_from_file {
my ($metadata_ref, $file) = @_;
my $metadata = %$metadata_ref; # deref
if ( ! -r $file ) {
return -1;
}
print "Reading exiftool from $file...\n" if $debuggin;
undef my %exifToolInfo;
my $exifToolInfo = $exifTool->ImageInfo($file);
foreach (keys %$exifToolInfo) {
print "$_ => $$exifToolInfo{$_}\n" if $debuggin;
}
# preserve/convert the info we are interested in
if ( defined $$exifToolInfo{DateTimeOriginal} ) {
$metadata{"dc:date"} = &exif_date_to_iso8601($$exifToolInfo{DateTimeOriginal});
# Local time nicer print
$exifTool->Options(DateFormat => '%Y-%m-%d %H:%M');
my $info = $exifTool->ImageInfo($file, 'DateTimeOriginal');
$metadata{"cap:local-time"} = $$info{'DateTimeOriginal'};
}
if ( defined $$exifToolInfo{ImageDescription} ) {
$metadata{"dc:title"} = $$exifToolInfo{ImageDescription};
$metadata{"dc:description"} = $$exifToolInfo{ImageDescription};
}
if ( defined $$exifToolInfo{City}
or defined $$exifToolInfo{'Province-State'}
or defined $$exifToolInfo{'Country-PrimaryLocationName'})
{
my $blurb = "";
$blurb .= $$exifToolInfo{City} if defined $$exifToolInfo{City};
if (defined $$exifToolInfo{'Province-State'}){
$blurb .= ", " if $blurb ne "";
$blurb .= $$exifToolInfo{'Province-State'} ;
}
if (defined $$exifToolInfo{'Country-PrimaryLocationName'}){
$blurb .= ", " if $blurb ne "";
$blurb .= $$exifToolInfo{'Country-PrimaryLocationName'} ;
}
$metadata{"dc:coverage"} = $blurb;
}
if ( defined $$exifToolInfo{UserComment} and $$exifToolInfo{UserComment} =~ m/visibility:/) {
if ( $$exifToolInfo{UserComment} =~ m/visibility:public/ ) { $metadata{"cap:visibility"} = 'public'; }
elsif ( $$exifToolInfo{UserComment} =~ m/visibility:friends/ ) { $metadata{"cap:visibility"} = 'friends'; }
else { $metadata{"cap:visibility"} = 'private'; }
}
# copy any interesting exif data we found into %metadata
foreach my $field (@field_order) {
next unless $field =~ /^exif:/ or $field =~ /^iptc:/;
my $shortfield = $field; $shortfield =~ s/^exif:|^iptc://;
$metadata{$field} = $$exifToolInfo{$shortfield} if defined $$exifToolInfo{$shortfield};
}
# if we have GPS coord, add numeric value for it into %metadata
if (defined( $metadata{"exif:GPSLatitudeRef"} ) and
defined( $metadata{"exif:GPSLatitude"} ) and
defined( $metadata{"exif:GPSLongitudeRef"} ) and
defined( $metadata{"exif:GPSLongitude"} ) )
{
$exifTool->Options(CoordFormat => '%.15f');
my $exifToolInfo = $exifTool->ImageInfo($file, 'GPSLatitude', 'GPSLongitude', 'GPSLatitudeRef', 'GPSLongitudeRef');
my $lat = $$exifToolInfo{'GPSLatitude'};
my $long = $$exifToolInfo{'GPSLongitude'};
if ( $$exifToolInfo{'GPSLatitudeRef'} eq "South") { $lat *= -1; }
if ( $$exifToolInfo{'GPSLongitudeRef'} eq "West") { $long *= -1; }
$metadata{'cap:GPSLatitudeNum'} = $lat;
$metadata{'cap:GPSLongitudeNum'} = $long;
# Nicer for html print
my $coord = "";
$coord = sprintf('%.2f', $$exifToolInfo{'GPSLatitude'});
$coord .= "° " . $$exifToolInfo{'GPSLatitudeRef'};
$coord .= " - " . sprintf('%.2f', $$exifToolInfo{'GPSLongitude'});
$coord .= "° " . $$exifToolInfo{'GPSLongitudeRef'};
$metadata{'cap:coord'} = $coord;
}
return 0;
}
sub exif_date_to_iso8601 {
# convert an exif-format date to an iso-8601 format date
# e.g.: "2001:09:08 15:21:29" -> 2001-09-08T15:21:29Z
my $date = shift;
$date =~ s/"//g;
$date =~ s/:/-/;
$date =~ s/:/-/; # just the first two, not all of them
$date =~ s/ /T/;
$date =~ s/$/Z/;
return $date;
}
### Index ###
sub print_index_header {
my $disclaimer = "";
$disclaimer .= qq{
\n};
if ( defined $collection{headerText} ) {
my $indexHeader = $collection{headerText};
$indexHeader .= qq{
\n};
$indexHeader .= qq{
\n};
$indexHeader .= qq{
\n};
$indexHeader .= qq{
\n};
# adsense bottom
$indexHeader .= $collection{adBottomText} if ( defined $collection{adBottomText} );
$indexHeader .= qq{\n} if ($opt{publicIndex});
$indexHeader .= qq{
$opt{indextitle}
\n\n};
$indexHeader .= qq{
\n\n};
$indexHeader =~ s/%%title%%/$opt{indextitle}/g;
$indexHeader =~ s/%%year%%/$currentYear/g;
$indexHeader =~ s/%%date%%/$hour:$minute:$second $dayOfMonth\/$currentMonth\/$currentYear/g;
my $keywords = "photo,travel,video," . $opt{indextitle};
$indexHeader =~ s/%%keywords%%/$keywords/g;
print INDEX $indexHeader;
if ($opt{publicIndex}){
my $publicIndexHeader = $indexHeader;
$publicIndexHeader =~ s//$disclaimer/g;
# Replace the Overview link with public one for the year albums
$publicIndexHeader =~ s/([0-9][0-9][0-9][0-9])\/Overview\.html/$1\/Overview.public.html/g;
print PUBLICINDEX $publicIndexHeader;
}
}
}
sub generate_index_entry {
my ($metadata_ref, $file) = @_;
my $metadata = %$metadata_ref; # deref
$file = &relativize_dir_path( $opt{indexfile} ) . $file;
# @@ should get this stuff from user prefs
my $src = $file; $src =~ s/\.jpg$/-sq.jpg/;
my $href = $file; $href =~ s/\.jpg$/-sm.html/;
my $text = "";
$text .= qq{
\n};
return $navList;
}
sub generate_html_body {
# generate the main html with the photo, the info and the map for a file
my ($metadata_ref, $file, $isPublicPhoto) = @_;
my $bodyText = "";
$bodyText .= qq{
\n};
$bodyText .= qq{
\n};
$bodyText .= qq{
\n};
# Main column
$bodyText .= qq{
\n};
if ( $file =~ /-sm\.jpg$/ or $file =~ /-med\.jpg$/ or $file =~ /\.flv$/) {
# Adsense Bottom
$bodyText .= $collection{adBottomText} if ( defined $collection{adBottomText} );
}
$bodyText .= &generate_html_body_small( \%metadata, $file, $isPublicPhoto ) if ( $file =~ /-sm\.jpg$/);
$bodyText .= &generate_html_body_med( \%metadata, $file, $isPublicPhoto ) if ( $file =~ /-med\.jpg$/);
$bodyText .= &generate_html_body_large( \%metadata, $file, $isPublicPhoto ) if ( ($file =~ /.jpg$/) and !($file =~ /-(sm|med)\.jpg$/) );
$bodyText .= &generate_html_body_video( \%metadata, $file, $isPublicPhoto ) if ( $file =~ /.flv$/);
$bodyText .= qq{
\n\n};
# Adsense Right
$bodyText .= qq{
\n};
$bodyText .= $collection{adRightText} if ( defined $collection{adRightText} );
$bodyText .= qq{
\n};
$bodyText .= qq{
\n};
$bodyText .= qq{
\n};
$bodyText .= qq{
\n};
return $bodyText;
}
sub generate_html_body_small {
my ($metadata_ref, $file, $isPublicPhoto) = @_;
my $basefile = &basename( $file );
my $html ="";
$html .= qq{
\n};
$html .= qq{
} . $metadata{"dc:title"} . qq{
\n\n};
# Photo
my $href = &basename( $file );
$href =~ s/-sm\.jpg$/-med.html/;
$href =~ s/\.html$/.public.html/ if ($isPublicPhoto);
$html .= qq{
\n};
$html .= qq{
\n};
$html .= qq{ \n};
$html .= qq{
\n\n};
$html .= qq{
\n\n};
# Google map if gps coordinates available
if (defined( $metadata{"cap:GPSLatitudeNum"} ) and
defined( $metadata{"cap:GPSLongitudeNum"} ) )
{
$html .= qq{
\n};
$html .= $metadata{"infotext"};
my $navbar = &build_html_navbar( $file );
$navbar =~ s/med\.html/med.public.html/ if ($isPublicPhoto);
$html .= qq{
• $navbar •
\n};
$html .= qq{
\n\n};
$html .= qq{
\n\n};
return $html;
}
sub generate_html_body_med {
my ($metadata_ref, $file, $isPublicPhoto) = @_;
my $title = $metadata{"dc:title"} || "No title specified";
my $basefile = &basename( $file );
my $coord = "";
$coord = $metadata{"cap:coord"} if (defined( $metadata{"cap:coord"} ) );
my $html ="";
$html .= qq{
\n};
$html .= qq{
} . $metadata{"dc:title"} . qq{
\n\n};
# Photo
my $href = &basename( $file );
$href =~ s/-med\.jpg$/-sm.html/;
$href =~ s/\.html$/.public.html/ if ($isPublicPhoto);
$html .= qq{
\n};
$html .= qq{ \n};
$html .= qq{
\n\n};
# Add details and links
$html .= qq{
\n};
$html .= $metadata{"infotext"};
my $navbar = &build_html_navbar( $file );
$navbar =~ s/sm\.html/sm.public.html/ if ($isPublicPhoto);
$html .= qq{
\n\n};
return $html;
}
sub generate_html_body_large {
# deprecated
my ($metadata_ref, $file, $isPublicPhoto) = @_;
my $title = $metadata{"dc:title"} || "No title specified";
my $basefile = &basename( $file );
my $html ="";
$html .= qq{
\n\n};
#@@ table
$html .= &generate_html_fields_list( \%metadata );
return $html;
}
sub generate_html_body_video {
my ($metadata_ref, $file, $isPublicPhoto) = @_;
my $html ="";
my $videoJpg = $file;
$videoJpg =~ s/\.flv$/.jpg/;
my $videoJpgWm = $videoJpg;
$videoJpgWm =~ s/\.jpg$/-wm.jpg/;
my $videoDir = $opt{videosDir};
$videoDir =~ s,/$,,; # strip trailing '/'
my $pathToVideo = $ENV{PWD} . "/" . $videoDir;
$pathToVideo =~ s/\/web\/www//;
print "Path to video : ".$pathToVideo."\n" if $debuggin;
$html .= qq{
\n};
# Photo
my $href = &basename( $file );
$href =~ s/-med\.jpg$/-sm.html/;
$href =~ s/\.html$/.public.html/ if ($isPublicPhoto);
$html .= qq{
Click to start video
\n};
$html .= qq{
\n};
$html .= qq{ \n\n};
$html .= qq{
\n\n};
# Details
$html .= qq{
\n};
$html .= $metadata{"infotext"};
$html .= qq{
\n\n};
# Close post
$html .= qq{
\n\n};
return $html;
}
sub generate_html_footer {
my ($metadata_ref, $file, $isPublicPhoto) = @_;
my $footerText = "";
if ( defined $collection{footerText} ) {
$footerText .= $collection{footerText};
&update_time(); # Update value for current time
$footerText =~ s/%%year%%/$currentYear/g;
$footerText =~ s/%%date%%/$hour:$minute:$second $dayOfMonth\/$currentMonth\/$currentYear/g;
# After everything because of supid IE bug -> http://vidmar.net/weblog/archive/2005/08/22/2121.aspx
if ( defined $file and
$file =~ /-sm\.jpg$/ and
defined( $metadata{"cap:GPSLatitudeNum"} ) and
defined( $metadata{"cap:GPSLongitudeNum"} ) )
{
my $mapText .= qq{ \n\n};
$mapText .= qq{\n};
$footerText =~ s/<\/body>/$mapText/g;
}
}
return $footerText;
}
sub build_html_navbar {
# generate HTML code to be used as the navbar for individual HTML images
my $file = shift;
my $navbar = "";
my $basefile = &basename( $file );
my $origfile = $basefile;
$origfile =~ s/($variant_pat)?\.jpg$/.jpg/;
my $smfile = $basefile;
$smfile =~ s/($variant_pat)?\.jpg$/-sm.html/;
my $medfile = $basefile;
$medfile =~ s/($variant_pat)?\.jpg$/-med.html/;
my $original_hw = "";
if ( defined $metadata{"exif:ExifImageWidth"} and defined $metadata{"exif:ExifImageLength"} ) {
$original_hw .= $metadata{"exif:ExifImageWidth"} . "x" . $metadata{"exif:ExifImageLength"};
}
$navbar .= qq{Versions: };
$navbar .= qq{ } unless $file =~ /-sm\.jpg$/;
$navbar .= qq{Small};
$navbar .= qq{} unless $file =~ /-sm\.jpg$/;
$navbar .= qq{ |\n};
$navbar .= qq{ } unless $file =~ /-med\.jpg$/;
$navbar .= qq{Medium};
$navbar .= qq{} unless $file =~ /-med\.jpg$/;
$navbar .= qq{ |\n};
$navbar .= qq{ } unless $file =~ /[0-9][0-9]-[0-9][0-9]-[0-9][0-9]\.jpg$/;
$navbar .= qq{Large};
$navbar .= qq{} unless $file =~ /[0-9][0-9]-[0-9][0-9]-[0-9][0-9]\.jpg$/;
$navbar .= qq{ ($original_hw)};
$navbar .= qq{ ()};
return $navbar;
}
sub generate_infotext {
my ($metadata_ref, $file) = @_;
my $href = &basename( $file );
# to fix sm/med and public or not
$href =~ s/-sm\.jpg$/-med.html/;
my $coord = "";
$coord = $metadata{"cap:coord"} if (defined( $metadata{"cap:coord"} ) );
my $local_time = 'No time';
$local_time = $metadata{"cap:local-time"} if ( defined($metadata{"cap:local-time"}) );
# added test on $metadata{"exif:Model"} for digitalized pictures not from digital cameras
if ( ! defined( $metadata{"cap:cameraShortName"} ) and defined( $metadata{"exif:Model"} )) {
$metadata{"cap:cameraShortName"} = $metadata{"exif:Model"};
$metadata{"cap:cameraShortName"} =~ s/"//g;
}
if ( ! defined( $metadata{"t:camera"} ) and defined( $metadata{"exif:Model"} )) {
my $camera_model = $metadata{"exif:Model"};
$camera_model =~ s/"//g;
$metadata{"t:camera"} =
"http://www.amazon.com/exec/obidos/external-search?index=photo&keyword=" .
&uri_escape( $camera_model ) .
"&tag=geraldoskoboiny&Search=Search"; # -> PROFIT!
}
my $infotext = "";
# Title
$infotext .= qq{
} . $metadata{"dc:title"} . qq{
\n};
$infotext .= qq{
\n};
# Place
if ( defined($metadata{"iptc:City"}) ) {
my $city = $metadata{"iptc:City"};
$infotext .= qq{ $city, \n };
}
if ( defined($metadata{"iptc:Province-State"}) ) {
my $state = $metadata{"iptc:Province-State"};
$infotext .= qq{ $state, \n };
}
if ( defined($metadata{"iptc:Country-PrimaryLocationName"}) ) {
my $country = $metadata{"iptc:Country-PrimaryLocationName"};
$infotext .= qq{ $country\n };
}
#@@ replace with appropriate link ???
#$infotext .= qq{ ($coord)\n} if ( !($coord eq "") );
$infotext .= qq{ ($coord)\n} if ( !($coord eq "") );
$infotext .= qq{ \n};
# Time
$infotext .= qq{ Taken $local_time\n};
# Camera, focal and exposure info if sm file and available
if ( defined($metadata{"cap:cameraShortName"}) and defined($metadata{"t:camera"}) ) {
$infotext .= qq{ , with };
$infotext .= $metadata{"cap:cameraShortName"} . qq{};
}
$infotext .= qq{
\n};
return $infotext;
}
sub generate_html_fields_list {
my ($metadata_ref) = @_;
my $metadata = %$metadata_ref; # deref
my $fieldListText = "";
# Output all the fields
$fieldListText .= qq{
\n};
foreach my $field (@field_order) {
if ( defined $metadata{$field} ) {
next if $field =~ /^cap:/; # don't output captivate prefs
next if $field =~ /^dc:title/; # title is already printed
my $short_field = $field;
$short_field =~ s/^[^:]+://; # strip ns prefix
if ( $short_field =~ m/^[a-z]/ ) { # starts with lowercase char?
$short_field =~ s/./\U$&/; # initial-case it
}
if ( defined $uri_fields{$field} ) {
$fieldListText .= qq[
\n];
if ($short_field =~ /^Date/) { # nicer print for the date
my $newDate = $metadata{$field};
$newDate =~ tr/[TZ]/ /;
$fieldListText .= qq[
$newDate
\n];
} else {
$fieldListText .= qq[
$metadata{$field}
\n];
}
$fieldListText .= qq[
\n];
}
}
}
$fieldListText .= qq{
\n\n};
return $fieldListText;
}
### Output for videos ###
sub generate_video_html {
my ($video) = @_;
my $videoDir = $opt{videosDir};
$videoDir =~ s,/$,,; # strip trailing '/'
my $videoHtml = $video;
$videoHtml =~ s/\.flv$/.html/;
# Create Html page for current video
my $html_file = $videoDir . "/" . $videoHtml;
my $temp = $html_file . ".tmp";
print "Writing HTML to file ", $html_file, "\n" if $debuggin;
open( TEMP, "> $temp" ) or warn "error trying to write to temp html file, $temp: $!";
# Generate HTML
my $headerText = &generate_html_head( \%metadata, $video, 0 );
my $bodyText = &generate_html_body( \%metadata, $video, 0 );
my $footerText = &generate_html_footer;
# Print it in file
print TEMP $headerText;
print TEMP $bodyText;
print TEMP $footerText;
close( TEMP ) or warn "error closing temp file, $temp: $!";
&replace_file_iff_different( $html_file, $temp );
}
### Utils functions ###
sub replace_file_iff_different {
# replace $oldfile with $newfile only if its contents differ
# (to avoid updating files if nothing has changed)
my ($oldfile,$newfile) = @_;
# @@ should probably replace this with something else, in perl
my $rc = system( "cmp -s $oldfile $newfile" );
if ( $rc ) {
rename( $newfile, $oldfile ) or warn "error renaming file from $newfile to $oldfile: $!";
} else { # no diff from $oldfile, so nuke $newfile and keep $oldfile
unlink( $newfile ) or warn "error unlinking newfile $newfile: $!";
}
return $rc;
}
sub basename {
my $file = shift;
$file =~ s,.*/,,;
return $file;
}
sub dirname {
my $dir = shift;
$dir =~ s,[^/]+$,,;
return $dir;
}
sub prune_redundant_dirs {
my $path = shift;
1 while $path =~ s,[^\./][^/]+/\.\./,,;
1 while $path =~ s,\.\./[^\.][^/]+/,,;
return $path;
}
sub relativize_dir_path {
# return ../ or ../../ etc as needed to get from $path to $PWD
my $path = &prune_redundant_dirs( shift );
$path =~ s,^$ENV{PWD}/,,;
return "../" x $path =~ tr,/,,; # return a ../ for each dir remaining
}
sub uri_escape {
# buggy, needs work/replacement
my $text = shift;
$text =~ s/ /+/g;
return $text;
}
sub update_time {
($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime();
}