Automated Web Photo Galleries with iPhoto and Perl
Pages: 1, 2
Perl
Perl is often called "the duct tape of the Internet," and this application certainly fits that description. Now is the time to open an editor and create some Perl code. This code needs to go in a particular location to allow the server to process it correctly. My iMac server is currently running MacOS X 10.2.8, which uses Sendmail. Accordingly, the Perl program needs to be located in the /usr/adm/sm.bin directory. You'll need to create any directories along the way, such as adm and sm.bin.
The gist of the program is that we are going to receive an email message, which will have MIME attachments. Those attachments will have to be extracted from the message, processed, and stored in a database. Once we're done processing the email, we'll send back a message indicating how many images were processed and the URLs to those images.
#!/usr/bin/perl -w
# load in the modules
use MIME::Parser;
use MIME::Entity;
use DBI;
use Image::Magick;
use Mail::Mailer;
use strict;
# get DBI vars and related info
my %dbHash = (
"dbType" => 'mysql',
"dbName" => 'domain',
"dbHost" => 'localhost',
"dbUser" => 'myUser',
"dbPass" => 'myPass'
);
# DBI handles
my ($sth, $sql, $rv, $dbh, $dbData);
# set up DB connection
DBConnect(%dbHash)
or DBError("Died in Connect");
# MIME parsing vars
my ($i, $parser, $entity, $head, $preamble,
$epilogue, $num_parts, $part, $content_type,
$body, $tmp);
# extract the pieces out of the email message
$parser = MIME::Parser->new();
$parser->output_dir("data");
$entity = $parser->parse(\*STDIN);
$head = $entity->head;
$preamble = $entity->preamble;
$epilogue = $entity->epilogue;
# get the subject and use it as
# the category for all the photos
my $category = $head->get('Subject');
my $mailTo = $head->get('To');
my $mailFrom = $head->get('From');
chomp($category);
chomp($mailTo);
chomp($mailFrom);
# get the domain name from the To mailing address
my $domain = (split('@', $mailTo))[1];
# MIME vars
my ($bh, $filename, %file, @data, $title,
$comments, @url, $id);
# Image::Magick vars
my ($img, $imageData, $thumbData, $err);
# loop through the file attachments
$num_parts = $entity->parts;
for ($i = 1; $i < $num_parts; $i++) {
$part = $entity->parts($i);
$content_type = $part->mime_type unless
($part->mime_type =~ 'text');
$body = $part->as_string;
$bh = $part->bodyhandle;
$filename = $bh->path;
if (($i % 2) == 1) {
# handle the image file
$file{'image'} = $filename;
}
else {
# handle the data file and populate
# the database
$file{'data'} = $filename;
# init the data array
@data = ();
# open the data file and load into
# data array
open(\*FILE, "< $file{'data'}")
or die "Error opening $file{'data'}: $!";
while (<FILE>) {
chomp;
# ignore empty lines
push @data, $_ if length > 0;
}
close(FILE)
or warn "Error closing $file{'data'}: $!";
# extract title and comments from data array
$title = $data[0];
shift @data;
$comments = '';
$comments = join ' ', @data if @data;
# convert image to thumbnail
$img = new Image::Magick;
$err = $img->Read($file{'image'});
die "Can't read image file: $err\n" if $err;
$imageData = $img->ImageToBlob();
$err = $img->Scale(geometry=>"200x200");
die "Can not scale image file: $err" if $err;
$thumbData = $img->ImageToBlob();
# build, prepare and execute SQL command
$sql = "REPLACE INTO Gallery
(Date, Image, Thumb, Type, Title,
Comments, Category)
VALUES (NOW(), ?, ?, ?, ?, ?, ?)";
$sth = $dbh->prepare($sql)
or DBError("Died in prepare");
$sth->execute($imageData, $thumbData,
$content_type, $title, $comments,
$category)
or DBError("Died in execute");
# get the last inserted ID to build a URL
$sql = "SELECT ID FROM Gallery
WHERE ID=LAST_INSERT_ID()";
$sth = $dbh->prepare($sql)
or DBError("Died in prepare");
$sth->execute()
or DBError("Died in execute");
$dbData = $sth->fetchrow_hashref();
$sth->finish();
$id = $dbData->{'ID'};
push @url, "http://www.$domain/cgi-bin/" .
"gallery.pl?id=$id";
}
}
# disconnect from the database
DBDisconnect();
# remove files from data directory
$entity->purge;
# return an email message to the sender
my $mailer = Mail::Mailer->new("sendmail");
# get the number of images sent
my $count = @url;
# build the message body
my $text = "$count new image";
$text .= "s" if ($count > 1);
# build the header
$mailer->open(
{
Subject => "$text added to $domain gallery",
From => '<no-reply@' . $domain . '>',
To => $mailFrom
}
);
# print the message body
$text = "The following new image";
if ($count > 1) {
$text .= "s were";
}
else {
$text .= " was";
}
print $mailer "$text added:\n\n";
print $mailer "$_\n" for (@url);
print $mailer "\n";
# close the message
$mailer->close();
# DB connection subroutine
sub DBConnect {
# convert the list to a hash
my %dbHash = @_;
# data source name
my $dsn = "DBI:$dbHash{'dbType'}:" .
"$dbHash{'dbName'}:$dbHash{'dbHost'}";
# attributes
my %attr = (
PrintError => 0,
RaiseError => 1
);
# connection command
$dbh = DBI->connect($dsn, $dbHash{'dbUser'}, " .
$dbHash{'dbPass'}, \%attr) or
DBError("Cannot connect to $dsn");
}
# DB disconnect subroutine
sub DBDisconnect {
$dbh->disconnect()
or DBError("Cannot disconnect from DB");
}
# DB error subroutine
sub DBError {
my $message = shift;
# display a message
warn "$message\nError $DBI::err " .
"($DBI::errstr)\n";
}
Now we've accomplished getting the images into the database. That's all well and good, but the other half of the task is displaying them on a web site. Thankfully, we just got through the longer part. There are several gallery applications available that work with databases of images. This section handles extracting three thumbnail images to be used each day on a rotating basis. Each thumbnail is linked to its full-size image, which will be displayed in a new window when the thumbnail is selected. The HTML output is presented after the Perl code.
#!/usr/bin/perl -w
use CGI;
use DBI;
use HTML::Entities;
use strict;
$|++;
# local vars
my ($id, $comments, $file, $url);
# set file name
$file = '/Library/WebServer/WebSites/' .
'www.domain.com/include/gallery.shtml';
# get a CGI object
my $q = new CGI;
# DBI handles
my ($sth, $sql, $rv, $dbh, $dbData);
# DB connection values
my %dbHash = (
"dbType" => 'mysql',
"dbName" => 'domain',
"dbHost" => 'localhost',
"dbUser" => 'myUser',
"dbPass" => 'myPass',
"dbTable" => 'Gallery'
);
# connect to the database
DBConnect(%dbHash);
# build the sql command
$sql = "SELECT ID, Comments FROM
$dbHash{'dbTable'} ORDER BY RAND()
LIMIT 3";
# prepare and execute the statement
$sth = $dbh->prepare($sql)
or DBError("Died in prepare");
$sth->execute()
or DBError("Died in execute");
open(\*FILE, "> $file")
or die "Unable to open $file: $!";
while ($dbData = $sth->fetchrow_hashref) {
$id = $dbData->{'ID'};
$comments = $dbData->{'Comments'};
# set HTML nbsp if no comments
$comments = ' ' unless
(length($comments) > 0);
$url = "/cgi-bin/gallery.pl?id=$id";
# send the results to the output file
print FILE
$q->td(
{
-align=>'center'
},
$q->a(
{
-href=>$url,
-target=>'gallery'
},
$q->img(
{
-src=>"/cgi-bin/gallery.pl?" .
"id=$id;thumb=1",
-border=>0,
-alt=>$comments
}
)
),
$q->br(),
$comments
),
"\n";
}
$sth->finish();
close(FILE)
or warn "Unable to close $file: $!";
# DB connection subroutine
sub DBConnect {
# convert the list to a hash
my %dbHash = @_;
# data source name
my $dsn = "DBI:$dbHash{'dbType'}:" .
"$dbHash{'dbName'}:$dbHash{'dbHost'}";
# attributes
my %attr = (
PrintError => 0,
RaiseError => 1
);
# connection command
$dbh = DBI->connect($dsn, $dbHash{'dbUser'}, " .
$dbHash{'dbPass'}, \%attr) or
DBError("Cannot connect to $dsn");
}
# DB disconnect subroutine
sub DBDisconnect {
$dbh->disconnect()
or DBError("Cannot disconnect from DB");
}
# DB error subroutine
sub DBError {
my $message = shift;
# display a message
warn "$message\nError $DBI::err " .
"($DBI::errstr)\n";
}
Here is some sample output from the above code. The output has been prettied up, but renders the same in HTML.
<td align="center">
<a target="gallery"
href="/cgi-bin/gallery.pl?id=28">
<img alt="Georgetown Railroad" border="0"
src="/cgi-bin/gallery.pl?id=28;thumb=1" />
</a>
<br />
Georgetown Railroad
</td>
<td align="center">
<a target="gallery"
href="/cgi-bin/gallery.pl?id=85">
<img alt="Lakeside morning" border="0"
src="/cgi-bin/gallery.pl?id=85;thumb=1" />
</a>
<br />
Lakeside morning
</td>
<td align="center">
<a target="gallery"
href="/cgi-bin/gallery.pl?id=95">
<img alt="Elk in stream" border="0"
src="/cgi-bin/gallery.pl?id=95;thumb=1" />
</a>
<br />
Elk in stream
</td>
The actual output looks more like this:
Georgetown Railroad |
Lakeside morning |
Elk in stream |
Final Thoughts
While this application is still under development, the techniques are used to show a daily sample of pictures, eCards, and photos by category. There is also a database editor application that can be used to change images from one category to another, or to change comments for an image.
Mike Schienle has been using Macs since the day they took the Lisa computer out of his office in 1984. After 10 years of kicking shell scripts around, Perl became his language of choice, although he knows his way around several others. Mike runs Custom Visuals, LLC, a small company that dabbles in internet applications, website design/hosting and database integration.
Return to the Mac DevCenter
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 10 of 10.
-
File naming scheme for thousands of image files.
2004-05-10 23:11:26 idarmadi [Reply | View]
I've been doing image files management for quite a while, and keep thinking if there's a better way to name them.
Currently, I use AAA11111.jpg naming scheme. The first 3 letters will correspond to a specific info (photographers, quality (hi or low), etc), and the number (5 digit) will auto increment.
I used name such as "my vacation 01.jpg, my graduation with friends.jpg", etc. but I don't like it.
Anybody know a better naming scheme? Please advice.
regards,
id -
File naming scheme for thousands of image files.
2004-05-11 08:45:21 MSchienle [Reply | View]
I don't think a naming scheme is going to solve your issue. You need something that will allow you to search through categories, quality, photographer, date, etc. and then give you the name of the image. That's part of what iPhoto provides, but you may have to extend that to an actual database like FileMaker or MySQL to handle the query and point to the correct file.
Mike Schienle
-
I do something similar
2004-05-10 12:52:13 GoodDoug [Reply | View]
I use Python with the PyObjC bridge to do a photo album that reads directly from my ~/Pictures/iPhoto Library/AlbumData.xml which has my iPhoto data.
The nice thing about using that is I don't have to do any extra steps, every picture in iPhoto is automatically available on the web. I'm too lazy to export, now all I have to do is plug my camera in and all of my photos are on the web. They can also be viewed by library (even with smart libraries) which makes things a little bit easier. Clicking on a picture to view it downloads the complete picture... which was a concious decision on my part, as my family often wants access to the full size picture for printing or making desktops of. And I'm just too lazy to write the code to resize the images, I'll get around to it once someone says they need it...
This won't work for those people that only want to have some photos on the web, as all photos are available by default.
See the results at http://gooddoug.dyndns.org/cgi-bin/PyPhoto/PyPhoto.py
-
try using iPhoto with Gallery and iPhotoToGallery exporter
2004-05-08 20:29:28 ferfey [Reply | View]
This is what I use on my site (http://jeffandjuliedavis.com):
- Gallery (http://gallery.sourceforge.net)
- iPhotoToGallery (http://zwily.com/iphoto/)
- Mambo (http://mamboserver.com)
My workflow is this:
Take the pics (using a Canon 10D), import into iPhoto for modification then export (choosing the iPhotoToGallery plugin) and upload to the server.
This combination allows we to export fullsize images to my server (which is actually running the gallery scripts integrated into a Mambo CMS). Gallery then will create the thumbnails and scaled down versions plus it has acl abilities, voting, slideshows and purchasing prints via shutterfly.
You can use Gallery by itself but the new version of Gallery allows it to be embeded into mambo phpnuke and a few other CMS apps.
-
Website
2004-05-08 09:06:29 eddy [Reply | View]
What's the url to Rhonda's website with all the pics? -
Website
2004-05-08 11:17:13 MSchienle [Reply | View]
Hi Eddy,
Rhonda's site is called Lighthouse Moments. It's still in its infancy and the web gallery portion has a lot of work to go.
Mike Schienle






thanks!
psx