
|
|
|
Automate a Web Photo Gallery with iPhoto and Perl
If iPhoto is working nicely as your digital
shoebox, but you want to automate the process of creating web
galleries for your own server, here's a nifty setup
using sendmail, MySQL, and Perl
The Code
[Discuss (1) | Link to this hack] |
The CodePerl 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 Mac OS 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. TIPFor more Perl shenanigans under Mac OS X, take
a gander at
[Hack #19] [Hack #20], and [Hack #21].
The gist of the program is that we are going to receive an email
message, which will have MIME attachments. Those attachments need 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 to display 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 extracts three thumbnail images to be used each day on a
rotating basis. Each thumbnail is linked to its full-size image,
which is 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 preceding code. The output has
been prettied up, but it 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>
shows the actual output. Figure 1. Script output as it appears on the Web
Showing messages 1 through 1 of 1.
-
Script failure
2006-01-20 00:58:15
mo-tester
[View]
|
Showing messages 1 through 1 of 1.
|
|
O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website:
| Customer Service:
| Book issues:
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
|
|
I've tried to implement this script on my web server, which is solaris 9, sendmail 8.13, perl 5.8.7, ImageMagick 6.2.4 and MYSQL. All the components seem to be working fine, but the script errors as follows:
syntax error at /PROD/testing/process.pl line 58, near "<"
syntax error at /PROD/testing/process.pl line 61, near ">"
syntax error at /PROD/testing/process.pl line 61, near "'text')"
syntax error at /PROD/testing/process.pl line 82, near "<"
syntax error at /PROD/testing/process.pl line 82, near ">"
syntax error at /PROD/testing/process.pl line 130, near "}"
Execution of /PROD/testing/process.pl aborted due to compilation errors.
I don't know PERL that well, so, unable to fix these issues which maybe particular to my system.
Anyone know?