#!/usr/bin/perl -w # # Anand Natrajan (www.anandnatrajan.com) # Mon Jun 10 15:30:00 EST 2002 # # Program to display photos in multiple ways. use File::Basename; use File::Path; use POSIX ":sys_wait_h"; use Getopt::Long; use Time::Local; use IO::Handle; $| = 1; $MyName = basename($0); $MyName =~ s/.*\\//; # For Windows paths. # Valid formats for pictures. @PixForms = ("jpg", "JPG", "jpeg", "JPEG", "gif", "GIF"); # Valid formats for captions. @TxtForms = ("txt", "TXT", "htm", "HTM", "html", "HTML", "shtml", "SHTML", "php", "PHP"); # Valid formats for sounds. @SndForms = ("mid", "MID", "wav", "WAV", "mp3", "MP3"); # Default output types. @OutForms = ("table", "thumbnail", "slideshow"); # Create a default record for displaying pictures in various formats. This # built-in default will be overridden by defaults the user specifies from # the command-line, which in turn will be overridden by any # picture-specific requests in the order files. %PixRec = ( tableWidth => 250, # 250 pixels width tableHeight => 'a', # automatic height thumbnailWidth => 100, # 100 pixels width thumbnailHeight => 'a', # automatic height thumbnailCols => 5, # 5 columns per row slideshowWidth => 500, # 500 pixels width slideshowHeight => 'a', # automatic height slideshowInterval => 5 # 5 seconds before moving on ); @BackColours = ("8fbc8f", "b5a642", "cococo", "d8bfd8", "d9d9f3", "ebc79e", "ffffff"); @BackImages = ( "http://wp.netscape.com/assist/net_sites/bg/fabric/gray_fabric.gif", "http://wp.netscape.com/assist/net_sites/bg/fabric/pink_fabric.gif", "http://wp.netscape.com/assist/net_sites/bg/fabric/yellow_fabric.gif", "http://wp.netscape.com/assist/net_sites/bg/weave/lipurple_weave.gif", "http://wp.netscape.com/assist/net_sites/bg/weave/blue_weave.gif", "http://wp.netscape.com/assist/net_sites/bg/weave/yellow_weave.gif", "http://wp.netscape.com/assist/net_sites/bg/weave/bluewind_weave.gif", "http://wp.netscape.com/assist/net_sites/bg/weave/embossed_weave.gif", "http://wp.netscape.com/assist/net_sites/bg/weave/funkyblue_weave.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/gray_rock.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/red_rock.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/yellow_rock.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/blue_rock.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/multicolor1_rock.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/multicolor2_rock.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/multicolor3.gif", "http://wp.netscape.com/assist/net_sites/bg/rock/smblue_rock.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/blue_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/greenred_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/redgray_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/purpleblue_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/olivepink_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/purple_marble1.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/lavender_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/lavender_swirl_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/70s_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/purple_marble2.gif", "http://wp.netscape.com/assist/net_sites/bg/marble/tanblue_marble.gif", "http://wp.netscape.com/assist/net_sites/bg/metal/brushed_aluminum.gif", "http://wp.netscape.com/assist/net_sites/bg/metal/gray_aluminum.gif", "http://wp.netscape.com/assist/net_sites/bg/metal/corrugated_metal.gif", "http://wp.netscape.com/assist/net_sites/bg/stucco/gray_stucco.gif", "http://wp.netscape.com/assist/net_sites/bg/stucco/green_stucco.gif", "http://wp.netscape.com/assist/net_sites/bg/stucco/red_stucco.gif", "http://wp.netscape.com/assist/net_sites/bg/stucco/yellow_stucco.gif", "http://wp.netscape.com/assist/net_sites/bg/stucco/smgreen_stucco.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/blue_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/bluewhite_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/greenwhite_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/olive_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/purple_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/tan_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/teal_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/yellow_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/bluesand_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/orange_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/bright_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/lavender_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/multidot_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/olivered_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/peach_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/red_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/smblue_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/smbluewhite_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/summer_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/wind_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/paper/redwhite_paper.gif", "http://wp.netscape.com/assist/net_sites/bg/dots/grey_dots.gif", "http://wp.netscape.com/assist/net_sites/bg/dots/1960s_dots.gif", "http://wp.netscape.com/assist/net_sites/bg/dots/multicolor1_dots.gif", "http://wp.netscape.com/assist/net_sites/bg/water/raindrops_dark.gif", "http://wp.netscape.com/assist/net_sites/bg/water/raindrops_light.gif", "http://wp.netscape.com/assist/net_sites/bg/water/clouds.gif" ); my $Now = localtime; sub Usage { print STDERR < [-o[ut] ]+ [-order ] [-clean] EOF if ($#_ > -1) { print STDERR < where is an album containing pictures and captions. The above line will create /index.html. Browsing that file will show your pictures. -help : Print this help screen. -about : Print information about the author. -v[erbose] : Turn verbose mode on. -debug : Turn debug mode on. Warning! Copious output. : Find picture files in . Can be specified multiply. Will recurse through sub-directories. -o[ut] : Display results in form only. Default is all formats. Formats can be appended with colon-separated parameters, e.g., table:: thumbnail::: slideshow::: where can be a number, a percentage, 'a' for automatic or empty for default can be a number, a percentage, 'a' for automatic or empty for default can be a number, 'a' or empty for default can be a number, 'a' or empty for default -order : Sort pictures in the order in in each album. Default is alphanumerical. should contain a list of names, one per line, with optional formats after spaces. Format is same as in -out flag. -clean : Cleans specified albums of previous run of $MyName and terminates. -cd : Makes the specified albums "CD-ready", i.e., creates a /../autorun.inf file. EOF } exit(1); } sub About { print STDERR <$file") or die "Couldn't open $file: $!\n"; &Dprint("Writing $file.\n"); print FILE $str; close FILE; } ### MAIN ### &GetOptions("help" => \$opt_help, "about" => \$opt_about, "debug", "v|verbose", "o|out=s@", "order=s", "clean" => \$opt_clean, "cd" => \$opt_cd) or &Usage; &Usage(1) if ($opt_help); &About if ($opt_about); $opt_v = 1 if (defined($opt_debug)); @opt_d = @ARGV; if (defined($opt_clean)) { my @ProbeDirs = @opt_d; # Start with the specified directories, then spiral in and remove junk # as we go through. The logic to add directories to traverse is used # below as well for actual work. while ($#ProbeDirs >= 0) { my $end = ($#ProbeDirs == 0) ? "" : "s"; &Dprint("Cleaning " . ($#ProbeDirs + 1) . " album$end.\n"); my $dir = shift(@ProbeDirs); &Vprint("Cleaning $dir.\n"); # Remove all the files for auto-running on a CD. unlink("$dir/../autorun.inf"); &Dprint("\tCleaning $dir/../autorun.inf.\n"); unlink("$dir/$MyName.bat"); &Dprint("\tCleaning $dir/$MyName.bat.\n"); opendir(DIR, $dir) or die "Can't open album $dir!\n"; foreach my $file (readdir(DIR)) { next if ($file eq "." or $file eq ".."); # Skip the obvious. # All our files are .html files prefixed with a '.'. Except # for index.html, which we'll overwrite. if ($file eq "index.html" or $file =~ /\.[a-zA-Z0-9]*\.html/) { unlink("$dir/$file"); &Dprint("\tCleaning $dir/$file.\n"); } # Add sub-directories to the list of directories we should probe. push(@ProbeDirs, "$dir/$file") if (-d "$dir/$file"); } closedir DIR; # ... and that's that. } exit(0); } my $opt_table = undef; my $opt_thumbnail = undef; my $opt_slideshow = undef; if (defined(@opt_o)) { foreach my $outform (@opt_o) { # The output formats may contain ':'-separated format suggestions. # For example, "-o table:34:57%"... says that for the table format, # widths should be 34 and heights should be 57%. If present, these # requests override the default table style. if ($outform =~ /^table/) { $opt_table = 1; # See if ':'-separated parameters are added to this parameter. my ($option, $width, $height) = split(/:/, $outform); # if not defined, stick with defaults. if (defined($width) and $width !~ /^\s*$/) { if ($width =~ /^a/) { $PixRec{'tableWidth'} = 'a'; # automatic width selection. } else { # Hope that's a number or percentage. $PixRec{'tableWidth'} = $width; } } # if not defined, stick with defaults. if (defined($height) and $height !~ /^\s*$/) { if ($height =~ /^a/) { $PixRec{'tableHeight'} = 'a'; # automatic width selection. } else { # Hope that's a number or percentage. $PixRec{'tableHeight'} = $height; } } } elsif ($outform =~ /^thumbnail/) { $opt_thumbnail = 1; # See if ':'-separated parameters are added to this parameter. my ($option, $width, $height, $cols) = split(/:/, $outform); # if not defined, stick with defaults. if (defined($width) and $width !~ /^\s*$/) { if ($width =~ /^a/) { $PixRec{'thumbnailWidth'} = 'a'; # automatic width selection. } else { # Hope that's a number or percentage. $PixRec{'thumbnailWidth'} = $width; } } # if not defined, stick with defaults. if (defined($height) and $height !~ /^\s*$/) { if ($height =~ /^a/) { $PixRec{'thumbnailHeight'} = 'a'; # automatic width selection. } else { # Hope that's a number or percentage. $PixRec{'thumbnailHeight'} = $height; } } # if not defined, stick with defaults. if (defined($cols) and $cols !~ /^\s*$/) { if ($cols !~ /^a/) # else stick with defaults. { # Hope that's a number. $PixRec{'thumbnailCols'} = $cols; } } } elsif ($outform =~ /^slideshow/) { $opt_slideshow = 1; # See if ':'-separated parameters are added to this parameter. my ($option, $width, $height, $secs) = split(/:/, $outform); # if not defined, stick with defaults. if (defined($width) and $width !~ /^\s*$/) { if ($width =~ /^a/) { $PixRec{'slideshowWidth'} = 'a'; # automatic width selection. } else { # Hope that's a number or percentage. $PixRec{'slideshowWidth'} = $width; } } # if not defined, stick with defaults. if (defined($height) and $height !~ /^\s*$/) { if ($height =~ /^a/) { $PixRec{'slideshowHeight'} = 'a'; # automatic width selection. } else { # Hope that's a number or percentage. $PixRec{'slideshowHeight'} = $height; } } # if not defined, stick with defaults. if (defined($secs) and $secs !~ /^\s*$/) { if ($secs !~ /^a/) # else stick with defaults. { # Hope that's a number. $PixRec{'slideshowInterval'} = $secs; } } } else { print STDERR "\"$outform\" is an invalid output format.\n\n" and &Usage; } } } else { $opt_table = $opt_thumbnail = $opt_slideshow = 1; } $TotalPictures = 0; @ProbeDirs = @opt_d; push(@ProbeDirs, undef); # This sentinel value indicates we should start putting parent dir links. $Uplink = 0; # The style below is put on each HTML page generated. $Style = < EOF # The signature below is put on each HTML page generated. $Signature = < $Now EOF # Figure out all the directories we need to probe. # Search each of those directories to make up the HTML pages. $PixPattern = "(" . join("|", @PixForms) . ")"; while ($#ProbeDirs >= 0) { # Set initial values for each section in each kind of output. my $MainPreHTML = ""; my $MainHTML = ""; my $MainNavHTML = ""; my $TableHTML = ""; my $ThumbnailHTML = ""; my $SlideShowHTML = ""; my $SlideShowNavHTML = ""; # Set the initial style for each kind of picture. my %Rec = %PixRec; my $CurrentPictures = 0; my $end = ($#ProbeDirs == 0) ? "" : "s"; &Dprint("Checking " . ($#ProbeDirs + 1) . " album$end.\n"); my $dir = shift(@ProbeDirs); if (!defined($dir)) { $Uplink = 1; next; } &Vprint("Checking $dir for pictures and albums.\n"); my $bgcolour = $BackColours[rand(@BackColours)]; $MainPreHTML .= < $dir $Style

Display of $dir


EOF if (defined($opt_slideshow)) { $MainNavHTML .= <Slide Show View] EOF } if (defined($opt_table)) { $MainNavHTML .= <Table View] EOF } $bgcolour = $BackColours[rand(@BackColours)]; $TableHTML .= < Table View of $dir $Style

Table View of $dir


EOF if (defined($opt_thumbnail)) { $MainNavHTML .= <Thumbnail View] EOF } $bgcolour = $BackColours[rand(@BackColours)]; $ThumbnailHTML .= < Thumbnail View of $dir $Style

Thumbnail View of $dir


EOF $MainNavHTML .= "
\n"; if ($Uplink) { $MainHTML .= <<up one level>
EOF } # If an order file is specified, look for it in each directory. Else, # just assume user wants alphanumeric sorting of entries. my @entries = (); my $found = 0; if (defined($opt_order)) { &Vprint("\tOrdering files in $dir.\n"); if (-e "$dir/$opt_order") { &Dprint("\t\tFound order file $dir/$opt_order.\n"); $found = 1; open(ORDER, "$dir/$opt_order") or die "Can't open order file $dir/$opt_order!\n"; foreach () { chomp; s/^\s+//; s/\s+$//; my ($entry, $rest) = split; # Right here, we need to parse "rest" and look at them for # stylistic hints. Also, we need to create an associative # array to store the hints for each file. push (@entries, $entry); } close ORDER; } else { warn "\t\tCouldn't find order file $dir/$opt_order!\n"; } } if (!$found) { &Dprint("\t\tNo ordering for $dir.\n"); opendir(DIR, $dir) or die "Can't open album $dir!\n"; foreach my $file (readdir(DIR)) { push(@entries, $file); } closedir DIR; @entries = sort @entries; } foreach my $file (@entries) { next if ($file eq "." or $file eq ".."); # Skip the obvious. # Need the existence check below if order is specified, really. if (!-e "$dir/$file") { warn "Couldn't find file $dir/$file in $dir/$opt_order!\n"; next; } # Add sub-directories to the list of directories we should probe. if (-d "$dir/$file") { &Dprint("\tFound album $dir/$file.\n"); push(@ProbeDirs, "$dir/$file"); $MainHTML .= <$file
EOF next; } next if (!-f "$dir/$file"); # Neither a directory, nor a file... # Pick an image for the slideshow. my $bgimage = $BackImages[rand(@BackImages)]; # Consider only those pictures that fit the proper extension pattern. next if ($file !~ /\.$PixPattern$/); &Dprint("\tFound picture $dir/$file.\n"); # Check if those pictures have corresponding captions. my $caption = undef; $file =~ m/(.*)\.(.*)/; my ($fname, $fext) = ($1, $2); foreach my $ext (@TxtForms) { if (-f "$dir/$fname.$ext") { $caption = "$fname.$ext"; &Dprint("\t\tFound caption $dir/$caption.\n"); last; } } &Dprint("\t\tFound no caption for $dir/$file.\n") if (!defined($caption)); $caption = (defined($caption) ? "src=$caption" : ""); my $width = ""; my $height = ""; # If width is specified, use it, else trust the browser. if ($Rec{'tableWidth'} ne 'a') { $width = "width=$Rec{'tableWidth'}"; &Dprint("\t\t\tSet table width to $width for $dir/$file.\n"); } # If height is specified, use it, else trust the browser. if ($Rec{'tableHeight'} ne 'a') { $height = "height=$Rec{'tableHeight'}"; &Dprint("\t\t\tSet table height to $height for $dir/$file.\n"); } if ($CurrentPictures % 2 == 0) { $TableHTML .= <
EOF } else { $TableHTML .= < EOF } $width = ""; $height = ""; # If width is specified, use it, else trust the browser. if ($Rec{'thumbnailWidth'} ne 'a') { $width = "width=$Rec{'thumbnailWidth'}"; &Dprint("\t\t\tSet thumbnail width to $width for $dir/$file.\n"); } # If height is specified, use it, else trust the browser. if ($Rec{'thumbnailHeight'} ne 'a') { $height = "height=$Rec{'thumbnailHeight'}"; &Dprint("\t\t\tSet thumbnail height to $height for $dir/$file.\n"); } # cols cannot be specified here - it is ignored. Can't have the # number of cols changing with every picture. $ThumbnailHTML .= < EOF if ($CurrentPictures % $Rec{'thumbnailCols'} == $Rec{'thumbnailCols'} - 1) { $ThumbnailHTML .= < EOF } my $PrevPicture = $CurrentPictures - 1; $PrevPicture = "slideshow" if ($PrevPicture == -1); $NextPicture = $CurrentPictures + 1; my $SlideShowHTML = ""; # This is the per-picture page. $width = ""; $height = ""; $interval = $Rec{'slideshowInterval'}; # If width is specified, use it, else trust the browser. if ($Rec{'slideshowWidth'} ne 'a') { $width = "width=$Rec{'slideshowWidth'}"; &Dprint("\t\t\tSet slideshow width to $width for $dir/$file.\n"); } # If height is specified, use it, else trust the browser. if ($Rec{'slideshowHeight'} ne 'a') { $height = "height=$Rec{'slideshowHeight'}"; &Dprint("\t\t\tSet slideshow height to $height for $dir/$file.\n"); } # If interval is specified, use it, else use the default. if ($Rec{'slideshowInterval'} ne 'a') { $interval = $Rec{'slideshowInterval'}; &Dprint("\t\t\tSet slideshow interval to $interval for $dir/$file.\n"); } $bgcolour = $BackColours[rand(@BackColours)]; $SlideShowHTML .= < Slide Show View of $dir $Style

Slide Show View of $dir, #$CurrentPictures


       
$Signature EOF &write("$dir/.$CurrentPictures.html", $SlideShowHTML) if (defined($opt_slideshow)); $CurrentPictures++; } $end = ($CurrentPictures == 1) ? "" : "s"; &Vprint("\tFound $CurrentPictures picture$end in $dir.\n"); $TotalPictures += $CurrentPictures; $MainPreHTML .= $MainNavHTML if ($CurrentPictures > 0); $MainHTML = $MainPreHTML . $MainHTML; $MainHTML .= < $CurrentPictures picture$end in $dir. EOF # If an index.txt file already exists, then include it in as a frame. if (-e "$dir/index.txt") { &Dprint("\t\tFound caption $dir/index.txt.\n"); $MainHTML .= < EOF } elsif (-e "$dir/index.TXT") { &Dprint("\t\tFound caption $dir/index.TXT.\n"); $MainHTML .= < EOF } $MainHTML .= < $Signature EOF $TableHTML .= <
$Signature EOF $ThumbnailHTML .= < $Signature EOF $bgcolour = $BackColours[rand(@BackColours)]; # Check if a sound file is present for this directory. If it is, then # start playing sound with the first slide show page. my $sound = undef; foreach my $ext (@SndForms) { if (-f "$dir.$ext") { $sound = basename($dir); $sound = "$sound.$ext"; &Dprint("\t\tFound tune $sound in dir $dir.\n"); last; } } &Dprint("\t\tFound no tune for $dir.\n") if (!defined($sound)); if (defined($sound)) { my $soundfile = "." . $sound . ".html"; &Dprint("\t\tGenerating tune file $soundfile for tune $sound.\n"); my $SlideShowSoundHTML = ""; $SlideShowSoundHTML .= < Slide Show Sound for $dir
<bgsound src=../$sound loop=infinite controls=smallconsole>
EOF &write("$dir/$soundfile", $SlideShowSoundHTML) if (defined($opt_slideshow)); $sound = < sound = window.open('$soundfile', 'sound', 'height=5,width=175,menubar=no,location=no,resizable=yes,scrollbars=no,alwaysRaised=yes'); EOF } else { $sound = ''; } $SlideShowHTML .= < Slide Show View of $dir $Style $sound

Slide Show View of $dir

Beginning now...
On any page, click on...
The left arrow to see the previous picture.
The right arrow to see the next picture.
The up arrow to exit the slide show.
   
A number in the navigation box to see the corresponding picture.
$Signature EOF $SlideShowNavHTML .= < Slide Show Navigation Bar of $dir $Style
EOF for (my $i = 0; $i < $CurrentPictures; $i++) { my $text = &prime($i) ? $i : "•"; $SlideShowNavHTML .= <$text EOF } $SlideShowNavHTML .= <
EOF # Write out all the output files except the intermediate ones for the # slide show, which should have been written out already. &write("$dir/index.html", $MainHTML); &write("$dir/.table.html", $TableHTML) if (defined($opt_table)); &write("$dir/.thumbnail.html", $ThumbnailHTML) if (defined($opt_thumbnail)); if (defined($opt_slideshow)) { &write("$dir/.slideshow.html", $SlideShowHTML); link("$dir/.slideshow.html", "$dir/.$CurrentPictures.html"); # or warn "Couldn't write to $dir/.$CurrentPictures.html: $!\n"; &Dprint("Linking $dir/.$CurrentPictures.html to start.\n"); &write("$dir/.slideshownav.html", $SlideShowNavHTML); } # Write out the files to make this directory "CD-ready". Notice we're # going to write autorun.inf and batch files in each subdirectory as # well, though it's only the root directory that needs it. That's all # right... it makes the logic simpler and it allows for people to tear # out pieces of their pictures and put only those on a CD... provided # they remember to copy the relevant autorun.inf files as well. if (defined($opt_cd)) { my $base = basename($dir); &write("$dir/../autorun.inf", "[autorun]\nopen=$base\\$MyName.bat\n"); &write("$dir/$MyName.bat", "\@echo off\n$base\\index.html\n"); } } my $end = ($TotalPictures == 1) ? "" : "s"; &Vprint("Found $TotalPictures picture$end totally.\n");