root/FlickrPublicPhotos/trunk/FlickrPublicPhotos.pl

Revision 123, 8.9 kB (checked in by ogawa, 3 years ago)

Change img_url according to Flickr's changes.
Be compatible with MT3.2 plugin interface.

  • Property svn:keywords set to Id
Line 
1# A plugin for adding "FlickrPublicPhotos" container and related tags
2#
3# $Id$
4#
5# This software is provided as-is. You may use it for commercial or
6# personal use. If you distribute it, please keep this notice intact.
7#
8# Copyright (c) 2005 Hirotaka Ogawa
9
10package MT::Plugin::FlickrPublicPhotos;
11use strict;
12use vars qw($VERSION);
13
14$VERSION = '0.22';
15
16my $plugin;
17eval {
18    require MT::Plugin;
19    $plugin = new MT::Plugin({
20        name => 'FlickrPublicPhotos',
21        description => 'Add FlickrPublicPhotos container and related tags.',
22        doc_link => 'http://as-is.net/hacks/2005/05/flickrpublicphotos_plugin.html',
23        author_name => 'Hirotaka Ogawa',
24        author_link => 'http://profile.typekey.com/ogawa/',
25        version => $VERSION
26        });
27    MT->add_plugin($plugin);
28};
29
30use MT::Template::Context;
31use MT::Util qw(offset_time_list);
32
33MT::Template::Context->add_container_tag('FlickrPublicPhotos' => \&photos);
34MT::Template::Context->add_tag('FlickrPublicPhotoTitle' => \&photo_title);
35MT::Template::Context->add_tag('FlickrPublicPhotoURL' => \&photo_url);
36MT::Template::Context->add_tag('FlickrPublicPhotoImgURL' => \&photo_img_url);
37MT::Template::Context->add_tag('FlickrPublicPhotoUploadDate' => \&date_upload);
38MT::Template::Context->add_tag('FlickrPublicPhotoTakenDate' => \&date_taken);
39MT::Template::Context->add_tag('FlickrPublicPhotoOwnerName' => \&owner_name);
40
41# Load photos via FlickrAPI
42sub load_photos_fapi {
43    my ($user) = @_;
44    my $flickr = new MT::Plugin::FlickrPublicPhotos::API();
45    return $flickr->photos($user);
46}
47
48# Load and cache photos
49sub load_photos {
50    my ($user, $refresh) = @_;
51    require MT::PluginData;
52    my $pd = MT::PluginData->load({ plugin => $plugin->name,
53                                    key => $user });
54    if (!$pd) {
55        $pd = new MT::PluginData();
56        $pd->plugin($plugin->name);
57        $pd->key($user);
58    }
59    my $data = $pd->data() || {};
60    my $now = time;
61    if (!defined($data->{last_updated}) || !defined($data->{photos}) ||
62        ($now - $data->{last_updated} >= $refresh)) {
63        my @photos = eval { load_photos_fapi($user); };
64        # if FlickrAPI call fails, reuse cache
65        if (!$@ || !defined($data->{photos})) {
66            $data->{photos} = \@photos;
67            $data->{last_updated} = $now;
68        }
69        $pd->data($data);
70        $pd->save or die $pd->errstr;
71    }
72    return @{$data->{photos}};
73}
74
75sub photos {
76    my ($ctx, $args) = @_;
77    my $user = $args->{user} or $ctx->error("'user' must be specified");
78    my $refresh = $args->{refresh} || 3600; # default: 1h
79    my @photos = eval { load_photos($user, $refresh); };
80    # if MT::PluginData is unavailable
81    @photos = eval { load_photos_fapi($user); } if $@;
82
83    my $lastn = $args->{lastn} || 0;
84    my $random = $args->{random} || 0;
85    if ($random) {
86        use List::Util qw(shuffle);
87        @photos = shuffle(@photos);
88        $lastn = $random;
89    }
90   
91    my $builder = $ctx->stash('builder');
92    my $tokens = $ctx->stash('tokens');
93    my $res = '';
94    my $i = 0;
95    for my $photo (@photos) {
96        last if $lastn && $i >= $lastn;
97        $ctx->stash('flickr_public_photo', $photo);
98        defined(my $out = $builder->build($ctx, $tokens))
99            or return $ctx->error($ctx->errstr);
100        $res .= $out;
101        $i++;
102    }
103    $res;
104}
105
106sub photo_title {
107    $_[0]->stash('flickr_public_photo')->title;
108}
109
110sub photo_url {
111    $_[0]->stash('flickr_public_photo')->url;
112}
113
114sub photo_img_url {
115    my ($ctx, $args) = @_;
116    my $url = $ctx->stash('flickr_public_photo')->img_url($args->{size} || 't');
117    my $cache = $args->{cache} or return $url;
118    $cache .= '/' unless $cache =~ m!/$!;
119    my $refresh = $args->{cache_refresh} || 86400; # default: 24h
120
121    my ($fname) = $url =~ m!^https?://.+/(.*)$!;
122
123    my $site_url = $ctx->stash('blog')->site_url;
124    $site_url .= '/' unless $site_url =~ m!/$!;
125    $site_url .= $cache . $fname;
126
127    my $path = $ctx->stash('blog')->site_path;
128    $path .= '/' unless $path =~ m!/$!;
129    $path .= $cache;
130    my $fmgr = $ctx->stash('blog')->file_mgr;
131    unless ($fmgr->exists($path)) {
132        # mkpath, and if can't return original url
133        $fmgr->mkpath($path) or return $url;
134    }
135    $path .= $fname;
136
137    my $mtime = 0;
138    my $now = time;
139    if ($fmgr->exists($path)) {
140        $mtime = (stat($path))[9];
141        return $site_url if ($refresh && ($now - $mtime < $refresh));
142    }
143
144    require LWP::UserAgent;
145    require HTTP::Request;
146    require HTTP::Response;
147    require HTTP::Date;
148    my $req = HTTP::Request->new(GET => $url);
149    my $ua = LWP::UserAgent->new;
150    $req->header('If-Modified-Since', HTTP::Date::time2str($mtime)) if $mtime;
151    my $rsp = $ua->request($req);
152    if ($rsp->is_success && $rsp->content) {
153        # put_data, and if can't return original url
154        $fmgr->put_data($rsp->content, $path) or return $url;
155        $url = $site_url;
156    } elsif ($rsp->code == 304) { # not modified
157        utime $now, $now, $path; # touch it
158        $url = $site_url;
159    }
160    $url;
161}
162
163sub date_upload {
164    my $args = $_[1];
165    my $t = $_[0]->stash('flickr_public_photo')->date_upload; # epoch format
166    my @ts = $args->{utc} ?
167        gmtime $t : offset_time_list($t, $_[0]->stash('blog_id'));
168    $args->{ts} = sprintf "%04d%02d%02d%02d%02d%02d", $ts[5]+1900, $ts[4]+1, @ts[3,2,1,0];
169    MT::Template::Context::_hdlr_date($_[0], $args);
170}
171
172sub date_taken {
173    my $args = $_[1];
174    $args->{ts} = $_[0]->stash('flickr_public_photo')->date_taken;
175    MT::Template::Context::_hdlr_date($_[0], $args);
176}
177
178sub owner_name {
179    $_[0]->stash('flickr_public_photo')->owner_name;
180}
181
182package MT::Plugin::FlickrPublicPhotos::Photo;
183
184sub new {
185    my ($class, $params) = @_;
186    $params ||= {};
187    bless $params, $class;
188}
189
190sub title {
191    $_[0]->{title} || '';
192}
193
194sub url {
195    my $this = shift;
196    my $url = 'http://www.flickr.com/photos/' . $this->{nsid} . '/' . $this->{id} . '/';
197    $url;
198}
199
200{
201my %sizes = (
202    sq => '_s',
203    t => '_t',
204    s => '_m',
205    m => '',
206    l => '_b',
207    o => '_o',
208    square => '_s',
209    thumbnail => '_t',
210    small => '_m',
211    medium => '',
212    large => '_b',
213    original => '_o',
214);
215sub img_url {
216    my $this = shift;
217    my ($size) = @_;
218    $size = $sizes{(lc $size) || 't'};
219    my $url = 'http://static.flickr.com/' . $this->{server} . '/' . $this->{id} . '_' . $this->{secret} . $size . '.jpg';
220    $url;
221}
222}
223
224# INPUT/OUTPUT: UNIX epoch format (should be converted to "YYYYMMDDHHMMSS")
225sub date_upload {
226    $_[0]->{dateupload} || '';
227}
228
229# INPUT: MySQL datetime in owner's localtime
230# OUTPUT: "YYYYMMDDHHMMSS" format
231sub date_taken {
232    my $date = $_[0]->{datetaken};
233    if ($date =~ /(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/) {
234        sprintf "%04d%02d%02d%02d%02d%02d", $1, $2, $3, $4, $5, $6;
235    } elsif ($date =~ /(\d\d\d\d)-(\d\d)/) {
236        sprintf "%04d%02d01000000", $1, $2;
237    } elsif ($date =~ /(\d\d\d\d)/) {
238        sprintf "%04d0101000000", $1;
239    }
240}
241
242sub owner_name {
243    $_[0]->{ownername} || '';
244}
245
246package MT::Plugin::FlickrPublicPhotos::API;
247use base 'Flickr::API';
248use XML::Parser::Lite::Tree::XPath;
249use constant API_KEY => '9765114fb37045ea8d2ca9d813e24b63';
250
251sub new {
252    my ($class, $params) = @_;
253    my $key = $params->{key} || API_KEY;
254    bless $class->SUPER::new({ key => $key }), $class;
255}
256
257sub resolve_nsid {
258    my $this = shift;
259    my ($uname) = @_;
260    return $uname if $uname =~ /^\d+\@N00$/;
261
262    my $rsp = ($uname =~ /^[^@]+@[^.]+\..+/) ?
263        $this->execute_method('flickr.people.findByEmail',
264                              { find_email => $uname }) :
265        $this->execute_method('flickr.people.findByUsername',
266                              { username => $uname });
267    die "Flickr request failed: " . $rsp->{error_message} . "\n"
268        unless $rsp->{success} == 1;
269
270    my $xpath = new XML::Parser::Lite::Tree::XPath();
271    $xpath->set_tree($rsp->{tree});
272    return ($xpath->select_nodes('//user'))[0]->{attributes}{id};
273}
274
275sub photos {
276    my $this = shift;
277    my ($uname) = @_;
278    my @photos = ();
279    my $nsid = $this->resolve_nsid($uname);
280    my ($page, $pages) = (1, 0);
281    my $xpath = new XML::Parser::Lite::Tree::XPath();
282    do {
283        my $rsp = $this->execute_method('flickr.people.getPublicPhotos',
284                                        { user_id => $nsid,
285                                          page => $page,
286                                          extras => 'license,date_upload,date_taken,owner_name,icon_server'
287                                          });
288        die "Flickr request failed: " . $rsp->{error_message} . "\n"
289            unless $rsp->{success} == 1;
290        $xpath->set_tree($rsp->{tree});
291        $pages ||= ($xpath->select_nodes('//photos'))[0]->{attributes}{pages};
292        my @photoNodes = $xpath->select_nodes('/photos/photo');
293        for my $node (@photoNodes) {
294            my $photo = new MT::Plugin::FlickrPublicPhotos::Photo();
295            $photo->{id} = $node->{attributes}{id};
296            $photo->{nsid} = $nsid;
297            $photo->{secret} = $node->{attributes}{secret};
298            $photo->{server} = $node->{attributes}{server};
299            $photo->{title} = $node->{attributes}{title};
300            $photo->{license} = $node->{attributes}{license};
301            $photo->{dateupload} = $node->{attributes}{dateupload};
302            $photo->{datetaken} = $node->{attributes}{datetaken};
303            $photo->{datetakengranularity} = $node->{attributes}{datetakengranularity};
304            $photo->{ownername} = $node->{attributes}{ownername};
305            $photo->{iconserver} = $node->{attributes}{iconserver};
306            push @photos, $photo;
307        }
308    } while ($page++ < $pages);
309    return @photos;
310}
311
3121;
Note: See TracBrowser for help on using the browser.