I have a small photo gallery on my website and in this presentation, I share
some steps I used in creating a nearly automatic workflow of getting
pictures from my camera to his gallery using Perl.
40. ...
open ARCHIVE, '>', $archive_image;
binmode ARCHIVE;
Error checking
print ARCHIVE $data;
is left as an exercise
close ARCHIVE;
for the reader.
my $src = new Image::Magick;
$src->Read($archive_image);
...
66. $_ == 0 && do {
$table .= quot;<tr><td align=center height=$CELLHEIGHT quot;
. quot;width=$CELLWIDTH valign=bottom>quot;;
last SWITCH;
}
67. $_ == 0 && do {
$table .= quot;<tr><td align=center height=$CELLHEIGHT quot;
. quot;width=$CELLWIDTH valign=bottom>quot;;
last SWITCH;
}
My only excuse is
that I wrote it a
really long time ago
83. my $parser = MIME::Parser->new();
$parser->output_dir( '/www/kentcowgill/photos/data' );
my $message = $parser->parse( *STDIN ) };
84. my $parser = MIME::Parser->new();
$parser->output_to_core(1);
$parser->output_dir( '/www/kentcowgill/photos/data' );
my $message;
eval { $message = $parser->parse( *STDIN ) };
if( $@ ){
my $results = $parser->results;
open ERR, '>', '/www/kentcowgill/photos/err.txt';
print ERR $results;
Additional error
close ERR;
} checking is left
as an exercise
for the reader.
116. [~]$ perldoc -f time
time Returns the number of non-leap
seconds since whatever time the
system considers to be the epoch,
suitable for feeding to quot;gmtimequot; and
quot;localtimequot;.
117. my $archivedir = '/www/kentcowgill/photos/arch';
my $thumbdir = '/www/kentcowgill/photos/thumbs';
my $pic_time = time;
my $archive_image
= $archivedir . '/' . $pic_time . '.jpg';
my $archive_thumb
= $thumbdir . '/' . $pic_time .'_thumb.jpg';
124. Or with a regex:
Believe it
use strict;
or not
use warnings;
my $date = scalar localtime $stamp;
$date =~ s{^w{3}s(w{3} )(?{$^N})s+(d+)(?{$,=$^R.$quot;.
$^N})s(d+)(?{$.=$^N;$@=$.>12?do{$.-=12;'pm'
}:$ .==12?'pm':'am'}):( d+)(?{$ /=quot;$ .:$ ^N$
@quot;}):d+s(d{4})(?{$==$ ^N})}{$,$quot;$=:$quot;$/}x;
# $date = 'Nov 13, 2007: 7:45pm';
130. CRUD
The four basic functions
of persistent storage.
Create Retrieve
Update Delete
131. Why?
• Makes later CRUD implementation easier.
• In the editing interface, just implement:
• Retrieve and Update
• Don't worry about Create
• Delete is just updating a record to
nothing.
132. What is CRUD without
the Create and Delete?
CRUD
135. my $dbh = Mysql->connect(
'localhost',
'photoblog',
$username,
$password,
);
my $sth = $dbh->query( qq{
insert into captions (caption_photo)
values ('$new_filename')}
); Bindinginput
parameters is left
$sth->finish;
as an exercise
for the reader.
149. my $src = Imager->new;
$src->read( file => $archive_image, type => 'jpeg' );
my $newimg = $src->copy();
my $curimg = $newimg->scale(
xpixels => 320,
ypixels => 240,
type => 'min',
);
my $gryimg = $curimg->convert( preset => 'grey' );
$gryimg->write( file => $current_image );
150. my ( $firstbase ) = $firstfile =~ m{^.+/(d+).jpg$};
$vars->{ mostrecent } = get_caption( $firstbase );
$vars->{ randomizer } .= int rand 9 for 1 .. 8;
151. Why?
• Browsers cache the image
• appending a random number
(i.e. quot;?12345678quot;) prevents caching
• It's an actual image, but it could be a CGI
• It could be dynamically generated
• Your web browser and my server won't
know the difference
• No caching, fresh request each time
152. [% IF mostrecent -%]
<table border=quot;0quot; class=quot;mostrecentquot;>
<tr><td class=quot;mostrecentquot;>
<img src=quot;/photos/mostrecent.jpg?[% randomizer %]quot;/>
</td></tr>
</table>
[% END -%]
156. Why?
• Having a chronological list is OK
• By assigning tags to photos, you won't have
to remember which page of 60 has:
• that picture of the dog from 3 months ago
• the picture of the bed you bought last year
• or...
170. AJAX (Asynchronous JavaScript and XML), or Ajax, is a
group of inter-related web development techniques used
for creating interactive web applications.
A primary characteristic is the increased responsiveness
and interactiveness of web pages achieved by
exchanging small amounts of data with the server quot;behind
the scenesquot; so that the entire web page does not have to
be reloaded each time the user performs an action.
This is intended to increase the web page's interactivity,
speed, functionality, and usability.
http://en.wikipedia.org/wiki/Ajax_(programming)
176. Load JQuery in your template
<script type = quot;text/javascriptquot;
src = quot;/script/jquery.jsquot;></script>
Right here
177. Create a Javascript Function
to switch the pictures
function swapem( pic ){
$('#caption').fadeOut( 'slow' );
$('#display').fadeOut( 'slow', function(){
getData( pic );
$('#display').html(
'<img src=quot;' + pic + 'quot; border=quot;0quot;>');
$('#display').show;
$('#display').fadeIn( 1500 );
Callback
});
}
178. Callback
A callback is executable code that is
passed as an argument to other code.
Because of the asynchronous nature of calls in
the JQuery library, using certain code as
callbacks helps ensure specific timing for events.
180. Call the AJAX code
<td align=quot;centerquot; valign=quot;bottomquot; class=quot;picquot;>
<a href=quot;javascript:swapem('[% pic.picture %]');quot;><img
src =quot;[% pic.thumb %]quot;
alt =quot;[% pic.caption %]quot;
title =quot;[% pic.caption %]quot;/></a>
<p class=quot;timestampquot;>[% pic.timestamp %]</p>
</td>
Right here
181. Create a CGI to handle the
AJAX call
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use CGI qw/:standard/;
Saved a lot of copying
use PhotoLib;
and pasting
...
182. Damian said it best:
• Place original code inline.
• Place duplicated code in a subroutine.
• Place duplicated subroutines in a module.
- Perl Best Practices, page 401
183. Create a CGI to handle the
AJAX call
...
my $pic = param('pic') || exit;
# $pic = '/photos/arch/1197770265.jpg';
$pic =~ s/[^d]+(d+)..*/$1/;
# $pic = '1197770265';
my $caption = get_caption( $pic );
$caption =~ s/''/'/g; # stoopid mysql
my @tags = get_tags( $pic );
...
184. Create a CGI to handle the
AJAX call
...
my $out = header();
$out .= qq{<span style=quot;font-family: tahoma; };
$out .= qq{quot;><p>$caption</p><p>More pictures: };
for my $tag( @tags ){
$out .= qq{<a href=quot;photos.cgi?tagged=$tagquot;>$tag</a> };
}
$out .= quot;</p>quot;;
print $out;
Should I have used
a template?
207. Time::Local(3) User Contributed Perl Documentation Time::Local(3)
NAME
Time::Local - efficiently compute time from local and GMT time
SYNOPSIS
$time = timelocal($sec,$min,$hour,$mday,$mon,$year);
$time = timegm($sec,$min,$hour,$mday,$mon,$year);
DESCRIPTION
These routines are the inverse of built-in perl functions localtime()
and gmtime(). They accept a date as a six-element array, and return
the corresponding time(2) value in seconds since the system epoch (Mid-
night, January 1, 1970 GMT on Unix, for example).
239. Exif Orientation Tag Values
Value 0th Row 0th Column
1 top left side
2 top right side
3 bottom right side
4 bottom left side
5 left side top
6 right side top
7 right side bottom
8 left side bottom
240.
241.
242.
243. Here is what the letter F would look like if
it were displayed by a program that
ignores the orientation tag:
1 2 3 4
888888 888888 88 88
88 88 88 88
8888 8888 8888 8888
88 88 88 88
88 88 888888 888888
5 6 7 8
8888888888 88 88 8888888888
88 88 88 88 88 88 88 88
88 8888888888 8888888888 88
From: http://sylvana.net/jpegcrop/exif_orientation.html
278. use Mac::AppleScript qw(RunAppleScript);
...
sub send_email {
my $scr;
my $msg = shift;
DEBUG quot;Attempting to send emailquot;;
($scr = <<quot; EOSquot;) =~ s/^s+//gm;
set fromAcct to quot;$FROM_ACCOUNTquot;
set toAcct to quot;$TO_ACCOUNTquot;
tell application quot;Mailquot;
repeat with acc in accounts
if display name of acc is fromAcct then
connect acc
repeat with ctc in contacts of acc
if display name of ctc is toAcct then
send ctc message quot;$msgquot; on account acc
end if
end repeat
disconnect acc
end if
end repeat
end tell
EOS
RunAppleScript($scr) or DEBUG quot;couldn't run applescriptquot;;
}
287. opendir my $indir, $picdir or die quot;Can't open directory - $!quot;;
send_img( $_ ) for grep { ! /^./ } readdir $indir;
sub send_img {
my $image = shift;
my $file = quot;$picdir/$imagequot;;
my $msg = MIME::Lite->new(
From => 'kent@c2group.net', To => 'XXXXX@c2group.net',
Subject => 'Here is my image', Type => 'multipart/mixed' );
$msg->attach(
Type => 'image/jpeg', Encoding => 'base64',
Path => $file, Filename => $image,
Disposition => 'attachment' );
$msg->send( 'smtp', 'localhost:2025' );
my $new_loc = $file; Use your own SMTP
$new_loc =~ s/send/sent/;
server, not the SSH tunnel
rename $file, $new_loc;
on my computer :)
}
304. use strict;
use warnings;
use Imager;
use Image::ExifTool;
my $src = Imager->new;
$src->read( file => $file );
my $exif = new Image::ExifTool;
my $info = $exif->SetNewValuesFromFile( $file );
$exif->SetNewValue(
'orientation' => 1,
Type => 'ValueConv'
);
...
$exif->WriteInfo( $new_file );
316. Strip it from the email
my $email = Email::MIME->new( $msg_text );
my $stripper
= Email::MIME::Attachment::Stripper->new( $email );
my @files = $stripper->attachments;
@files = grep {
$_->{content_type} eq 'image/jpeg'
} @files;
my $image_data
= @files ? $files[0]->{payload}
: decode_base64( $email->{parts}[0]->{body} );
317. Make a thumbnail
my $src = Imager->new;
$src->read( file => $archive_image, type => 'jpeg' );
my $tmbimg = $src->copy();
my $thumb = $tmbimg->scale(
xpixels => 64,
ypixels => 48,
type => 'min'
);
$thumb->write( file => $archive_thumb );
318. Make a greyscale image
my $curimg = $newimg->scale(
xpixels => 320,
ypixels => 240,
type => 'min',
);
my $gryimg = $curimg->convert( preset => 'grey' );
$gryimg->write( file => $current_image );
319. Add a caption
my $dbh = Mysql->connect(
'localhost',
'photoblog',
$username,
$password,
);
my $query = qq{
update captions set caption_text='$caption'
where caption_photo='$picture'};
my $sth = $dbh->query( $query );
320. Add tags
my $tags = param( 'tags' );
my @tags = split( /(?:s+|,)/, $tags );
for my $tag( @tags ){
my $ins = qq{
insert into tags( tag_photo, tag_name )
values( '$picture', '$tag' )quot;;};
$sth = $dbh->query( $ins );
}