File Coverage

blib/lib/Flickr/Simple2.pm
Criterion Covered Total %
statement 28 30 93.3
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 38 40 95.0


line stmt bran cond sub pod time code
1             package Flickr::Simple2;
2              
3 1     1   21236 use 5.008000;
  1         4  
  1         37  
4 1     1   6 use strict;
  1         1  
  1         33  
5 1     1   4 use warnings;
  1         6  
  1         65  
6              
7             our $VERSION = '0.03';
8              
9             our @ISA = qw(Exception::Class::TCF);
10              
11 1     1   5 use Carp;
  1         1  
  1         107  
12 1     1   5 use Digest::MD5 qw(md5_hex);
  1         2  
  1         45  
13 1     1   1005 use Exception::Class::TCF;
  1         17776  
  1         177  
14 1     1   1316 use Iterator::Simple qw(iterator);
  1         3183  
  1         78  
15 1     1   1029 use LWP::Simple;
  1         104300  
  1         12  
16 1     1   535 use URI;
  1         2  
  1         23  
17 1     1   482 use XML::Simple;
  0            
  0            
18              
19             =head1 NAME
20              
21             Flickr::Simple2 - A XML::Simple based Perl Flickr API.
22              
23             =head1 SYNOPSIS
24              
25             use Flickr::Simple2;
26             my $flickr =
27             Flickr::Simple2->new({
28             api_key => $cfg->param('Flickr.API_KEY'),
29             api_secret => $cfg->param('Flickr.API_SHARED_SECRET'),
30             auth_token => $cfg->param('Flickr.auth_token')
31             });
32              
33             =head1 DESCRIPTION
34              
35             A XML::Simple based Perl Flickr API.
36              
37             =head2 EXPORT
38              
39             None by default.
40              
41             =cut
42              
43             =head1 METHODS
44              
45             =head2 new
46              
47             =over 4
48              
49             my $flickr = Flickr::Simple2->new({
50             api_key => $cfg->param('Flickr.API_KEY'),
51             api_secret => $cfg->param('Flickr.API_SHARED_SECRET'),
52             auth_token => $cfg->param('Flickr.auth_token')
53             });
54              
55             api_key is your Flickr API key given by Flickr api_secret is your Flickr API secret key given by Flickr. It is used to sign certain Flickr API methods
56              
57             auth_token is optional for public/safe access to photos but is required for nonsafe/private photos or any Flickr API method requiring authentication
58              
59             =back
60              
61             =cut
62              
63             sub new {
64             my $proto = shift;
65             my $params_ref = shift;
66              
67             my $class = ref($proto) || $proto;
68              
69             my $self = {};
70              
71             %{ $self->{params} } = map { $_ => $params_ref->{$_} } keys %$params_ref;
72              
73             bless( $self, $class );
74             return $self;
75             }
76              
77             sub NEXTVAL {
78             $_[0]->();
79             }
80              
81             #-------------------------------------
82              
83             =head2 request
84              
85             =over 4
86              
87             An internal Flickr::Simple2 method used to communicate with the Flickr REST service. It uses XML::Simple for parsing of the XML data.
88              
89             =back
90              
91             =cut
92              
93             sub request {
94             my $self = shift;
95             my ( $flickr_method, $args, $xml_simple_args ) = @_;
96              
97             $xml_simple_args = {} unless $xml_simple_args;
98              
99             my $uri;
100             my @flickr_args =
101             ( 'method', $flickr_method, 'api_key', $self->{params}->{api_key} );
102              
103             foreach my $key ( sort { lc $a cmp lc $b } keys %$args ) {
104             next if ( $key =~ /^(?:api_key|method)/ );
105             push( @flickr_args, ( $key, $args->{$key} ) ) if defined $args->{$key};
106             }
107              
108             $uri = URI->new('http://api.flickr.com/services/rest');
109             $uri->query_form( \@flickr_args );
110             my $content = get($uri);
111              
112             if ($content) {
113             my $response = XMLin( $content, %$xml_simple_args );
114             return $response if $response;
115             }
116             }
117              
118             #------------------------------
119              
120             #------------------------------
121              
122             =head2 echo
123              
124             =over 4
125              
126             $echo_ref = $flickr->echo({ wild => "and crazy guy"});
127              
128             A simple method to send a message to Flickr and receive it back as a hash reference.
129              
130             Requires: hash reference containing valid name/value pairs, api_key
131             Returns: hash reference containing the response from Flickr
132              
133             =back
134              
135             =cut
136              
137             sub echo {
138             my $self = shift;
139             my $args = shift;
140              
141             return unless ($args);
142              
143             my $response =
144             $self->request( 'flickr.test.echo', $args,
145             { forcearray => 0, keeproot => 0 } );
146              
147             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
148             my %echo = map { $_ => $response->{$_} }
149             grep { !/^(?:method|api_key|stat)$/ } keys %$response;
150             return \%echo;
151             }
152             else {
153             $self->raise_error( $response, 'echo', 1 );
154             return;
155             }
156             }
157              
158             #------------------------------
159              
160             =head2 raise_error
161              
162             =over 4
163              
164             $self->raise_error($response, 'echo', 1);
165            
166             An internal method that sets the simple error object hash reference. The hash ref will be undefined if no error exists.
167              
168             =back
169              
170             =cut
171              
172             sub raise_error {
173             my $self = shift;
174             my ( $response, $calling_sub, $exit_now ) = @_;
175              
176             if ( defined $response->{err} ) {
177             $self->{'error'} = {
178             calling_sub => $calling_sub,
179             message => $response->{err}->{msg},
180             code => $response->{err}->{code}
181             };
182             }
183             else {
184             $self->{'error'} = { calling_sub => $calling_sub };
185             }
186              
187             return;
188             }
189              
190             =head2 clear_error
191              
192             =over 4
193              
194             $self->raise_error($response, 'echo', 1);
195            
196             An internal method that clears the simple error object hash reference. The hash ref will be undefined if no error exists.
197              
198             =back
199              
200             =cut
201              
202             sub clear_error {
203             my $self = shift;
204              
205             $self->{'error'} = undef;
206             }
207              
208             #------------------------------
209              
210             =head2 flickr_sign
211              
212             =over 4
213              
214             $self->flickr_sign({ method => 'flickr.auth.getFrob'});
215              
216             An internal method to sign the Flickr API method call with your Flickr API secret key.
217              
218             Requires: arguments to sign, flickr_api and flickr_api_secret
219             Returns: String containing the signed arguments
220              
221             =back
222              
223             =cut
224              
225             sub flickr_sign {
226             my $self = shift;
227             my $flickr_hash_ref = shift;
228              
229             my $sign;
230              
231             if ( $self->{params}->{api_secret} ) {
232             $sign = $self->{params}->{api_secret};
233             $flickr_hash_ref->{api_key} = $self->{params}->{api_key};
234              
235             foreach my $arg ( sort { $a cmp $b } keys %{$flickr_hash_ref} ) {
236             if ( defined( $flickr_hash_ref->{$arg} ) ) {
237             $sign .= $arg . $flickr_hash_ref->{$arg};
238             }
239             else {
240             $sign .= $arg . "";
241             }
242             }
243             }
244              
245             try {
246             if ($sign) {
247             $self->clear_error();
248             }
249             else {
250             throw 'Error';
251             }
252             }
253             catch 'Default' => sub { $self->raise_error( undef, 'flickr_sign', 1 ) };
254              
255             return md5_hex($sign) unless defined $self->{error};
256             }
257              
258             #------------------------------
259              
260             =head2 get_auth_frob
261              
262             =over 4
263              
264             my $frob = $flickr->get_auth_frob();
265              
266             A method to retrieve a Flickr frob, used for authentication.
267              
268             Requires: api_key and api_secret
269             Returns: String containing the frob
270              
271             =back
272              
273             =cut
274              
275             sub get_auth_frob {
276             my $self = shift;
277              
278             my $sign;
279             my $response;
280              
281             $sign = $self->flickr_sign( { method => 'flickr.auth.getFrob' } );
282             $response = $self->request(
283             'flickr.auth.getFrob',
284             { api_sig => $sign },
285             { forcearray => 0, keeproot => 0 }
286             );
287              
288             try {
289             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
290             $self->clear_error();
291             }
292             else {
293             throw 'Error';
294             }
295             }
296             catch 'Default' =>
297             sub { $self->raise_error( $response, 'get_auth_frob', 1 ) };
298              
299             return $response->{frob} unless defined $self->{error};
300             }
301              
302             =head2 get_auth_url
303              
304             =over 4
305              
306             my $auth_url = $flickr->get_auth_url($frob, 'read');
307              
308             A method to retrieve the url (webpage) for a Flickr user to authorize your application to use their account.
309              
310             Requires: frob, permissions (read/write), api_key and api_secret
311             Returns: String containing the URL
312              
313             =back
314              
315             =cut
316              
317             sub get_auth_url {
318             my $self = shift;
319             my ( $frob, $permissions ) = @_;
320              
321             my $uri;
322              
323             if ( $frob && $permissions ) {
324             my %args = (
325             'frob' => $frob,
326             'perms' => $permissions
327             );
328              
329             $args{api_sig} =
330             $self->flickr_sign( { frob => $frob, perms => $permissions } );
331             $args{api_key} = $self->{params}->{api_key};
332              
333             $uri = URI->new('http://flickr.com/services/auth');
334             $uri->query_form(%args);
335             }
336              
337             try {
338             if ($uri) {
339             $self->clear_error();
340             }
341             else {
342             throw 'Error';
343             }
344             }
345             catch 'Default' => sub { $self->raise_error( undef, 'get_auth_url', 1 ) };
346              
347             return $uri unless $self->{error};
348             }
349              
350             =head2 get_auth_token
351              
352             =over 4
353              
354             my $auth_token = $flickr->get_auth_token($frob);
355              
356             A method to retrieve a Flickr authorization token. Called after a user authorizes your application with the url returned from get_auth_url().
357              
358             Requires: frob, api_key and api_secret
359             Returns: String containing the authorization token
360            
361             =back
362              
363             =cut
364              
365             sub get_auth_token {
366             my $self = shift;
367             my $frob = shift;
368              
369             my $sign =
370             $self->flickr_sign( { frob => $frob, method => 'flickr.auth.getToken' } );
371             my $response = $self->request(
372             'flickr.auth.getToken',
373             { api_sig => $sign, frob => $frob },
374             { forcearray => 0, keeproot => 0 }
375             );
376              
377             try {
378             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
379             $self->clear_error();
380             $self->{params}->{'auth_token'} = $response->{'auth'}->{'token'};
381             }
382             else {
383             throw 'Error';
384             }
385             }
386             catch 'Default' =>
387             sub { $self->raise_error( $response, 'get_auth_token', 1 ) };
388              
389             return $response->{'auth'}->{'token'} unless defined $self->{error};
390             }
391              
392             =head2 check_auth_token
393              
394             =over 4
395              
396             my $auth = $flickr->check_auth_token()
397              
398             A method to validate a Flickr auth_token.
399              
400             Requires: auth_token, api_key and api_secret
401             Returns: true (1) if auth_token is valid else undef
402              
403             =back
404              
405             =cut
406              
407             sub check_auth_token {
408             my $self = shift;
409              
410             my $sign = $self->flickr_sign(
411             {
412             auth_token => $self->{params}->{auth_token},
413             method => 'flickr.auth.checkToken'
414             }
415             );
416             my $response = $self->request(
417             'flickr.auth.checkToken',
418             { api_sig => $sign, auth_token => $self->{params}->{auth_token} },
419             { forcearray => 0, keeproot => 0 }
420             );
421              
422             try {
423             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
424             $self->clear_error();
425             }
426             else {
427             throw 'Error';
428             }
429             }
430             catch 'Default' =>
431             sub { $self->raise_error( $response, 'check_auth_token', 1 ) };
432              
433             return 1 unless defined $self->{error};
434             }
435              
436             #------------------------------
437              
438             =head2 get_user_byEmail
439              
440             =over 4
441              
442             my $user_nsid = $flickr->get_user_byEmail('jason_froebe@yahoo.com');
443              
444             Retrieves the NSID of a Flickr user when given an email address
445              
446             Requires: email address, api_key
447             Returns: String containing the NSID of the user
448              
449             =back
450              
451             =cut
452              
453             sub get_user_byEmail {
454             my $self = shift;
455             my $user_email = shift;
456              
457             my $response = $self->request(
458             'flickr.people.findByEmail',
459             { find_email => $user_email },
460             { forcearray => 0, keeproot => 0 }
461             );
462              
463             try {
464             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
465             $self->clear_error();
466             }
467             else {
468             throw 'Error';
469             }
470             }
471             catch 'Default' =>
472             sub { $self->raise_error( $response, 'get_user_byEmail', 1 ) };
473              
474             return $self->get_user_info( $response->{user}->{nsid} )
475             unless $self->{error};
476             }
477              
478             =head2 get_user_byUsername
479              
480             =over 4
481              
482             my $user_nsid = $flickr->get_user_byUserName('jason_froebe');
483              
484             Retrieves the NSID of a Flickr user when given a Flickr username
485              
486             Requires: Flickr username, api_key
487             Returns: String containing the NSID of the user
488              
489             =back
490              
491             =cut
492              
493             sub get_user_byUserName {
494             my $self = shift;
495             my $username = shift;
496              
497             my $response = $self->request(
498             'flickr.people.findByUsername',
499             { username => $username },
500             { forcearray => 0, keeproot => 0 }
501             );
502              
503             try {
504             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
505             $self->clear_error();
506             }
507             else {
508             throw 'Error';
509             }
510             }
511             catch 'Default' =>
512             sub { $self->raise_error( $response, 'get_user_byUserName', 1 ) };
513              
514             return $self->get_user_info( $response->{user}->{nsid} )
515             unless $self->{error};
516             }
517              
518             =head2 get_user_byURL
519              
520             =over 4
521              
522             my $user_nsid = $flickr->get_user_byURL('http://www.flickr.com/photos/jfroebe/3214186886/');
523              
524             Retrieves the NSID of a Flickr user when given any URL (from Flickr website) associated with the user
525              
526             Requires: URL, api_key
527             Returns: String containing the NSID of the user
528              
529             =back
530              
531             =cut
532              
533             sub get_user_byURL {
534             my $self = shift;
535             my $url = shift;
536              
537             my $response = $self->request(
538             'flickr.urls.lookupUser',
539             { url => $url },
540             { forcearray => 0, keeproot => 0 }
541             );
542              
543             try {
544             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
545             $self->clear_error();
546              
547             }
548             else {
549             throw 'Error';
550             }
551             }
552             catch 'Default' =>
553             sub { $self->raise_error( $response, 'get_user_byUserName', 1 ) };
554              
555             return $self->get_user_info( $response->{user} ) unless $self->{error};
556             }
557              
558             =head2 get_user_info
559              
560             =over 4
561              
562             my $user_info = $flickr->get_user_info($user_nsid)
563              
564             Retrieves extensive information regarding a Flickr user
565              
566             Requires: NSID, api_key
567             Returns: Hash reference containing information about the user
568              
569             =back
570              
571             =cut
572              
573             sub get_user_info {
574             my $self = shift;
575             my $user = shift;
576              
577             my $response = $self->request(
578             'flickr.people.getInfo',
579             { user_id => $user->{id} },
580             { forcearray => 0, keeproot => 0 }
581             );
582              
583             try {
584             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
585             $self->clear_error();
586             }
587             else {
588             throw 'Error';
589             }
590             }
591             catch 'Default' =>
592             sub { $self->raise_error( $response, 'get_user_info', 1 ) };
593              
594             return $response->{person} unless $self->{error};
595             }
596              
597             #------------------------------
598              
599             =head2 get_license_info
600              
601             =over 4
602              
603             Retrieves the types of licenses used at Flickr
604              
605             Requires: api_key
606             Returns: Hash reference containing the license information
607              
608             =back
609              
610             =cut
611              
612             sub get_license_info {
613             my $self = shift;
614              
615             my $response = $self->request( 'flickr.photos.licenses.getInfo',
616             undef, { forcearray => ['id'], keeproot => 0, keyattr => ['id'] } );
617              
618             try {
619             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
620             $self->clear_error();
621             }
622             else {
623             throw 'Error';
624             }
625             }
626             catch 'Default' =>
627             sub { $self->raise_error( $response, 'get_license_info', 1 ) };
628              
629             return $response->{licenses}->{license} unless $self->{error};
630             }
631              
632             #------------------------------
633              
634             =head2 get_photo_exif
635              
636             =over 4
637              
638             $self->get_photo_exif($photo_id, $photo_secret);
639              
640             Retrieve the EXIF tags about a particular photo. Primarily used by get_photo_detail but can be used separately
641              
642             Requires: photo id, photo secret and api_key
643             Returns: Hash reference containing the EXIF tags
644              
645             =back
646              
647             =cut
648              
649             sub get_photo_exif {
650             my $self = shift;
651             my ( $photo_id, $photo_secret ) = @_;
652              
653             my $exif_ref;
654             my $response = $self->request(
655             'flickr.photos.getExif',
656             { photo_id => $photo_id, secret => $photo_secret },
657             { forcearray => 0, keeproot => 0 }
658             );
659              
660             try {
661             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
662             $self->clear_error();
663              
664             foreach my $hash_ref ( @{ $response->{photo}->{exif} } ) {
665             $exif_ref->{ $hash_ref->{tagspace} }->{ $hash_ref->{tag} } =
666             $hash_ref->{raw};
667             }
668             }
669             else {
670             throw 'Error';
671             }
672             }
673             catch 'Default' =>
674             sub { $self->raise_error( $response, 'get_photo_exif', 1 ) };
675              
676             return $exif_ref unless $self->{error};
677             }
678              
679             =head2 get_photo_detail
680              
681             =over 4
682              
683             my $photo_detail = $flickr->get_photo_detail($photo_id, $public_photos->{photo}->{$photo_id}->{secret});
684              
685             Retrieves extensive information regarding a particular photo.
686              
687             Requires: photo id, photo secret, api_key
688             Optional: api_secret for none safe/public photos
689             Returns: Hash reference containing photo information
690              
691             =back
692              
693             =cut
694              
695             sub get_photo_detail {
696             my $self = shift;
697             my ( $photo_id, $photo_secret ) = @_;
698              
699             my $response = $self->request(
700             'flickr.photos.getInfo',
701             { photo_id => $photo_id, secret => $photo_secret },
702             { forcearray => 0, keeproot => 0 }
703             );
704              
705             try {
706             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
707             $self->clear_error();
708              
709             $response->{photo}->{exif} =
710             $self->get_photo_exif( $photo_id, $photo_secret );
711             $response->{photo}->{tags} =
712             $self->get_photo_tags( $response->{photo}->{tags} );
713             $response->{photo}->{urls} =
714             $self->build_photo_urls( $photo_id, $response->{photo} );
715             }
716             else {
717             throw 'Error';
718             }
719             }
720             catch 'Default' =>
721             sub { $self->raise_error( $response, 'get_photo_detail', 1 ) };
722              
723             return $response->{photo} unless $self->{error};
724             }
725              
726             =head2 get_photos_page
727              
728             =over 4
729              
730             my $photos_iterator = $self->get_photos_page($user, $params_ref);
731              
732             Retrieves a "page" of photos from Flickr. Flickr groups photos in pages similar to their website.
733              
734             Returns: a hash reference for the current page of photos
735              
736             =back
737              
738             =cut
739              
740             sub get_photos_page {
741             my $self = shift;
742             my ( $user, $flickr_method, $params_ref ) = @_;
743              
744             my $args;
745             my $max_pages = 1;
746             my $auth_token;
747             my %temp_hash = ();
748              
749             $auth_token = $params_ref->{auth_token} if $params_ref->{auth_token};
750              
751             if ( $params_ref->{per_page} ) {
752             $max_pages = ( int $user->{photos}->{count} / $params_ref->{per_page} ) + 1;
753             }
754              
755             $args->{user_id} = $user->{nsid};
756              
757             if ( $params_ref->{photoset_id} ) {
758             $args->{photoset_id} = $params_ref->{photoset_id};
759             }
760              
761             if ( $params_ref->{safe_search} ) {
762             if ( $params_ref->{safe_search} eq 'safe' ) {
763             $args->{safe_search} = 1;
764             }
765             elsif ( $params_ref->{safe_search} eq 'moderate' ) {
766             $args->{safe_search} = 2;
767             }
768             elsif ( $params_ref->{safe_search} eq 'restricted' ) {
769             $args->{safe_search} = 3;
770             }
771             }
772              
773             if ( $params_ref->{extras} ) {
774             $args->{extras} = $params_ref->{extras};
775             }
776             else {
777             $args->{extras} =
778             "license,date_upload,date_taken,owner_name,icon_server,original_format,last_update,geo,tags,machine_tags,o_dims,views,media";
779             }
780              
781             $args->{per_page} = $params_ref->{per_page} if $params_ref->{per_page};
782              
783             if ( $params_ref->{page} ) {
784             $args->{page} = $params_ref->{page};
785             }
786             else {
787             $args->{page} = 1;
788             }
789              
790             if ($auth_token) {
791             $args->{auth_token} = $auth_token;
792             %temp_hash = %$args;
793             $temp_hash{method} = $flickr_method;
794             }
795              
796             iterator {
797             if ($auth_token) {
798             $temp_hash{page} = $args->{page};
799             $args->{api_sig} = $self->flickr_sign( \%temp_hash );
800             }
801              
802             my $response =
803             $self->request( $flickr_method, $args,
804             { forcearray => 0, keeproot => 0 } );
805              
806             try {
807             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
808             $self->clear_error();
809             }
810             else {
811             throw 'Error';
812             }
813             }
814             catch 'Default' =>
815             sub { $self->raise_error( $response, 'get_photos_page', 1 ) };
816              
817             $max_pages = $response->{photos}->{pages}
818             if ( exists $response->{photos} && exists $response->{photos}->{pages} );
819              
820             $args->{page}++ if ( $args->{page} < $max_pages );
821             return $response unless $self->{error};
822             }
823             }
824              
825             =head2 get_public_photos
826              
827             =over 4
828              
829             my $public_photos = $flickr->get_public_photos($user->{nsid}, { per_page => 3 });
830              
831             Retrieves a list of a Flickr user's public photos (simple info)
832              
833             Requires: NSID, hash reference containing the number of photos (per page) to retrieve at a time
834             Optional: Within the hash reference: safe_search, extras and page of photos to return
835              
836             From Flickr API:
837             safe_search (Optional)
838             Safe search setting:
839            
840             * 1 for safe.
841             * 2 for moderate.
842             * 3 for restricted.
843            
844             (Please note: Un-authed calls can only see Safe content.)
845             extras (Optional)
846             A comma-delimited list of extra information to fetch for each returned record. Currently supported fields are: license, date_upload, date_taken, owner_name, icon_server, original_format, last_update, geo, tags, machine_tags, o_dims, views, media.
847             per_page (Optional)
848             Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500.
849             page (Optional)
850             The page of results to return. If this argument is omitted, it defaults to 1.
851              
852             Returns: Hash reference containing photos and simple photo details (photo secret for example)
853              
854             =back
855              
856             =cut
857              
858             sub get_public_photos {
859             my $self = shift;
860             my $user = shift;
861             my $params_ref = shift;
862              
863             ${ $self->{licenses} } = $self->get_license_info() unless $self->{licenses};
864              
865             my $photos_iterator =
866             $self->get_photos_page( $user, 'flickr.people.getPublicPhotos',
867             $params_ref );
868              
869             $self->{error} = undef;
870             my $photo_page = $photos_iterator->next;
871             my @photo_ids = keys %{ $photo_page->{photos}->{photo} };
872              
873             iterator {
874             my $photo_id = shift @photo_ids;
875              
876             unless ( defined $photo_id ) {
877             $photo_page = $photos_iterator->next;
878              
879             if ( defined $photo_page ) {
880             @photo_ids = keys %{ $photo_page->{photos}->{photo} };
881             $photo_id = shift @photo_ids;
882             }
883             }
884              
885             if ( defined $photo_id ) {
886             $photo_page->{photos}->{photo}->{$photo_id}->{urls} =
887             $self->build_photo_urls( $photo_id,
888             $photo_page->{photos}->{photo}->{$photo_id} );
889              
890             $photo_page->{photos}->{photo}->{$photo_id}->{photo_id} = $photo_id;
891              
892             return $photo_page->{photos}->{photo}->{$photo_id};
893             }
894             }
895             }
896              
897             =head2 build_photo_urls
898              
899             =over 4
900              
901             my $photo_urls_ref = $self->build_photo_urls($photo_id, $response->{photos}->{photo}->{$photo_id} )
902              
903             From Flickr API:
904             http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg
905             http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg
906             http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)
907             s small square 75x75
908             t thumbnail, 100 on longest side
909             m small, 240 on longest side
910             - medium, 500 on longest side
911             b large, 1024 on longest side (only exists for very large original images)
912             o original image, either a jpg, gif or png, depending on source format
913              
914             An internal method that builds the various size photo URLs for a particular photo. Called primarily from get_public_photos. Does not guarantee that the photo URLs are valid.
915              
916             Requires: photo id, reference containing details of the photo, api_key
917             Returns: Hash reference containing the photo URLs
918              
919             =back
920              
921             =cut
922              
923             sub build_photo_urls {
924             my $self = shift;
925             my ( $photo_id, $photo_ref ) = @_;
926              
927             my $urls = {};
928              
929             my $base_url = sprintf "http://farm%s.static.flickr.com/%s/%s_",
930             $photo_ref->{farm},
931             $photo_ref->{server},
932             $photo_id;
933              
934             $urls->{smallsquare} = sprintf "%s%s_s.jpg", $base_url, $photo_ref->{secret};
935             $urls->{thumbnail} = sprintf "%s%s_t.jpg", $base_url, $photo_ref->{secret};
936             $urls->{small} = sprintf "%s%s_m.jpg", $base_url, $photo_ref->{secret};
937             $urls->{medium} = sprintf "%s%s.jpg", $base_url, $photo_ref->{secret};
938             $urls->{large} = sprintf "%s%s_b.jpg", $base_url, $photo_ref->{secret};
939              
940             if ( $base_url
941             && $photo_ref->{originalsecret}
942             && $photo_ref->{originalformat} )
943             {
944             $urls->{original} = sprintf "%s%s_o.%s", $base_url,
945             $photo_ref->{originalsecret}, $photo_ref->{originalformat};
946             }
947              
948             return $urls;
949             }
950              
951             =head2 get_photo_sizes
952              
953             =over 4
954              
955             Retrieves photo size information regarding a particular photo.
956              
957             Requires: photo id, api_key
958             Returns: Hash reference containing URLs and other information regarding the sizes of the photo
959              
960             =back
961              
962             =cut
963              
964             sub get_photo_sizes {
965             my $self = shift;
966             my $photo_id = shift;
967              
968             my $url = {};
969             my $response = $self->request(
970             'flickr.photos.getSizes',
971             { photo_id => $photo_id },
972             { forcearray => 0, keeproot => 0 }
973             );
974              
975             try {
976             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
977             $self->clear_error();
978             }
979             else {
980             throw 'Error';
981             }
982             }
983             catch 'Default' =>
984             sub { $self->raise_error( $response, 'get_photo_detail', 1 ) };
985              
986             return $response->{sizes}->{size} unless $self->{error};
987             }
988              
989             =head2 get_photo_sizes
990              
991             =over 4
992              
993             Retrieves a list of photosets & pools a particular photo belongs to.
994              
995             Requires: photo id, api_key
996             Returns: Hash reference regarding the contexts of the photo
997              
998             =back
999              
1000             =cut
1001              
1002             sub get_photo_contexts {
1003             my $self = shift;
1004             my $photo_id = shift;
1005              
1006             my $context = {};
1007             my $response = $self->request(
1008             'flickr.photos.getAllContexts',
1009             { photo_id => $photo_id },
1010             { forcearray => 0, keeproot => 0 }
1011             );
1012              
1013             try {
1014             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
1015             $self->clear_error();
1016              
1017             foreach my $tmp_context ( keys %$response ) {
1018             next unless ( $tmp_context =~ /^(?:set|pool)$/ );
1019             $context->{$tmp_context}->{ $response->{$tmp_context}->{title} } =
1020             $response->{$tmp_context}->{id};
1021             }
1022             }
1023             else {
1024             throw 'Error';
1025             }
1026             }
1027             catch 'Default' =>
1028             sub { $self->raise_error( $response, 'get_photo_detail', 1 ) };
1029              
1030             return $context unless $self->{error};
1031             }
1032              
1033             =head2 get_photo_tags
1034              
1035             =over 4
1036              
1037             $self->get_photo_tags($tags_ref);
1038              
1039             Sanitizes the tags structure that is part of the flickr.photos.getInfo response. Used by get_photo_detail subroutine.
1040              
1041             Requires tags structure returned by flickr.photos.getInfo
1042             Returns a hash ref with tag_name => "tagged by NSID" structure.
1043              
1044             =back
1045              
1046             =cut
1047              
1048             sub get_photo_tags {
1049             my $self = shift;
1050             my $tags = shift;
1051              
1052             my $tmp_tags;
1053              
1054             foreach my $tag_id ( keys %{ $tags->{tag} } ) {
1055             if ( exists $tags->{tag}->{$tag_id}
1056             && UNIVERSAL::isa( $tags->{tag}->{$tag_id}, "HASH" ) )
1057             {
1058             $tmp_tags->{$tag_id} = {
1059             'content' => $tags->{tag}->{$tag_id}->{content},
1060             'author' => $tags->{tag}->{$tag_id}->{author},
1061             'raw' => $tags->{tag}->{$tag_id}->{raw}
1062             };
1063             }
1064             else {
1065              
1066             # some Flickr accounts don't seem to be missing the tag id so we have to pretend we have one:
1067             $tmp_tags->{1234567890} = {
1068             'content' => $tags->{tag}->{content},
1069             'author' => $tags->{tag}->{author},
1070             'raw' => $tags->{tag}->{raw}
1071             };
1072             }
1073             }
1074              
1075             return $tmp_tags;
1076             }
1077              
1078             #------------------------------
1079              
1080             =head2 get_photoset_list
1081              
1082             =over 4
1083              
1084             Retrieves a list of photosets for a Flickr user
1085              
1086             Requires: user_id, api_key
1087             Returns: Hash reference containing a list and limited info of photosets for a user.
1088              
1089             =back
1090              
1091             =cut
1092              
1093             sub get_photoset_list {
1094             my $self = shift;
1095             my $user_id = shift;
1096              
1097             my $tmp_photoset_list_ref;
1098             my $response = $self->request(
1099             'flickr.photosets.getList',
1100             { user_id => $user_id },
1101             { forcearray => 0, keeproot => 0 }
1102             );
1103              
1104             try {
1105             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
1106             $self->clear_error();
1107              
1108             foreach my $photoset_id ( keys %{ $response->{photosets}->{photoset} } ) {
1109             $tmp_photoset_list_ref->{ $photoset_id } = {
1110             'id' => $photoset_id,
1111             'title' => $response->{photosets}->{photoset}->{$photoset_id}->{title},
1112             'primary' =>
1113             $response->{photosets}->{photoset}->{$photoset_id}->{primary},
1114             'photos' =>
1115             $response->{photosets}->{photoset}->{$photoset_id}->{photos},
1116             'secret' =>
1117             $response->{photosets}->{photoset}->{$photoset_id}->{secret},
1118             'farm' => $response->{photosets}->{photoset}->{$photoset_id}->{farm},
1119             'description' =>
1120             $response->{photosets}->{photoset}->{$photoset_id}->{description},
1121             'videos' =>
1122             $response->{photosets}->{photoset}->{$photoset_id}->{videos},
1123             'server' =>
1124             $response->{photosets}->{photoset}->{$photoset_id}->{server},
1125             };
1126             }
1127             } else {
1128             throw 'Error';
1129             }
1130             } catch 'Default' => sub { $self->raise_error( $response, 'get_photoset_list', 1 ) };
1131              
1132             return $tmp_photoset_list_ref unless $self->{error};
1133             }
1134              
1135             =head2 get_photoset_photos
1136              
1137             =over 4
1138              
1139             my $photoset_photos = $flickr->get_photoset_photos($user, { photoset_id => $photoset_id, per_page => 3 });
1140              
1141             Retrieves a list of a Flickr user's photoset photos (simple info)
1142              
1143             Requires: NSID, hash reference containing the number of photos (per page) to retrieve at a time
1144             Optional: Within the hash reference: safe_search, extras and page of photos to return
1145              
1146             From Flickr API:
1147             safe_search (Optional)
1148             Safe search setting:
1149            
1150             * 1 for safe.
1151             * 2 for moderate.
1152             * 3 for restricted.
1153            
1154             (Please note: Un-authed calls can only see Safe content.)
1155             extras (Optional)
1156             A comma-delimited list of extra information to fetch for each returned record. Currently supported fields are: license, date_upload, date_taken, owner_name, icon_server, original_format, last_update, geo, tags, machine_tags, o_dims, views, media.
1157             per_page (Optional)
1158             Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500.
1159             page (Optional)
1160             The page of results to return. If this argument is omitted, it defaults to 1.
1161              
1162             Returns: Hash reference containing photos and simple photo details (photo secret for example)
1163              
1164             =back
1165              
1166             =cut
1167              
1168             sub get_photoset_photos {
1169             my $self = shift;
1170             my $user = shift;
1171             my $params_ref = shift;
1172              
1173             ${ $self->{licenses} } = $self->get_license_info() unless $self->{licenses};
1174              
1175             my $photos_iterator =
1176             $self->get_photos_page( $user, 'flickr.photosets.getPhotos', $params_ref );
1177              
1178             $self->{error} = undef;
1179             my $photo_page = $photos_iterator->next;
1180             my @photo_ids = keys %{ $photo_page->{photoset}->{photo} };
1181              
1182             iterator {
1183             my $photo_id = shift @photo_ids;
1184              
1185             while ( defined $photo_id && $photo_id !~ m/^[[:digit:]]+$/ ) {
1186             $photo_id = shift @photo_ids;
1187             }
1188              
1189             unless ( defined $photo_id ) {
1190             $photo_page = $photos_iterator->next;
1191              
1192             if ( defined $photo_page ) {
1193             @photo_ids = keys %{ $photo_page->{photoset}->{photo} };
1194             $photo_id = shift @photo_ids;
1195             }
1196             }
1197              
1198             if ( defined $photo_id
1199             && defined $photo_page->{photoset}->{photo}->{$photo_id} )
1200             {
1201             $photo_page->{photoset}->{photo}->{$photo_id}->{urls} =
1202             $self->build_photo_urls( $photo_id,
1203             $photo_page->{photoset}->{photo}->{$photo_id} );
1204              
1205             $photo_page->{photoset}->{photo}->{$photo_id}->{photo_id} = $photo_id;
1206              
1207             return $photo_page->{photoset}->{photo}->{$photo_id};
1208             }
1209             }
1210             }
1211              
1212             =head2 get_photoset_info
1213              
1214             =over 4
1215              
1216             Retrieves information regarding a particular photo set
1217              
1218             Requires: photoset_id, api_key
1219             Returns: Hash reference containing information regarding a photo set.
1220              
1221             =back
1222              
1223             =cut
1224              
1225             sub get_photoset_info {
1226             my $self = shift;
1227             my $photoset_id = shift;
1228              
1229             my $tmp_photoset_info_ref;
1230             my $response = $self->request(
1231             'flickr.photosets.getInfo',
1232             { photoset_id => $photoset_id },
1233             { forcearray => 0, keeproot => 0 }
1234             );
1235              
1236             try {
1237             if ( defined $response->{'stat'} && $response->{'stat'} eq 'ok' ) {
1238             $self->clear_error();
1239             $tmp_photoset_info_ref = $response->{'photoset'};
1240             } else {
1241             throw 'Error';
1242             }
1243             }
1244             catch 'Default' =>
1245             sub { $self->raise_error( $response, 'get_photoset_info', 1 ) };
1246              
1247             return $tmp_photoset_info_ref unless $self->{error};
1248             }
1249              
1250             #--------------------------
1251              
1252             =head2 get_your_photos_not_in_set
1253              
1254             =over 4
1255              
1256             my $photos = $flickr->get_your_photos_not_in_set({ per_page => 3 });
1257              
1258             Retrieves a list of YOUR photos not currently in a photoset (simple info)
1259              
1260             Requires:
1261             Optional: Within the hash reference: safe_search, extras and page of photos to return
1262              
1263             From Flickr API:
1264             safe_search (Optional)
1265             Safe search setting:
1266            
1267             * 1 for safe.
1268             * 2 for moderate.
1269             * 3 for restricted.
1270            
1271             (Please note: Un-authed calls can only see Safe content.)
1272             extras (Optional)
1273             A comma-delimited list of extra information to fetch for each returned record. Currently supported fields are: license, date_upload, date_taken, owner_name, icon_server, original_format, last_update, geo, tags, machine_tags, o_dims, views, media.
1274             per_page (Optional)
1275             Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500.
1276             page (Optional)
1277             The page of results to return. If this argument is omitted, it defaults to 1.
1278              
1279             Returns: Hash reference containing photos and simple photo details (photo secret for example)
1280              
1281             =back
1282              
1283             =cut
1284              
1285             sub get_your_photos_not_in_set {
1286             my $self = shift;
1287             my $user = shift;
1288             my $params_ref = shift;
1289              
1290             ${ $self->{licenses} } = $self->get_license_info() unless $self->{licenses};
1291             $params_ref->{auth_token} = $self->{params}->{auth_token};
1292              
1293             my $photos_iterator =
1294             $self->get_photos_page( $user, 'flickr.photos.getNotInSet', $params_ref );
1295             $self->{error} = undef;
1296             my $photo_page = $photos_iterator->next;
1297             my @photo_ids = keys %{ $photo_page->{photos}->{photo} };
1298              
1299             iterator {
1300             my $photo_id = shift @photo_ids;
1301              
1302             while ( defined $photo_id && $photo_id !~ m/^[[:digit:]]+$/ ) {
1303             $photo_id = shift @photo_ids;
1304             }
1305              
1306             unless ( defined $photo_id ) {
1307             $photo_page = $photos_iterator->next;
1308              
1309             if ( defined $photo_page ) {
1310             @photo_ids = keys %{ $photo_page->{photos}->{photo} };
1311             $photo_id = shift @photo_ids;
1312             }
1313             }
1314              
1315             if ( defined $photo_id
1316             && defined $photo_page->{photos}->{photo}->{$photo_id} )
1317             {
1318             $photo_page->{photos}->{photo}->{$photo_id}->{urls} =
1319             $self->build_photo_urls( $photo_id,
1320             $photo_page->{photos}->{photo}->{$photo_id} );
1321              
1322             $photo_page->{photos}->{photo}->{$photo_id}->{photo_id} = $photo_id;
1323              
1324             return $photo_page->{photos}->{photo}->{$photo_id};
1325             }
1326             }
1327             }
1328              
1329             #------------------------------
1330             1;
1331              
1332             =head1 SEE ALSO
1333              
1334             Flickr API (http://flickr.com/services/api), XML::Simple
1335              
1336             http://froebe.net/blog/category/apis/flickr-apis/
1337              
1338             =head1 AUTHOR
1339              
1340             Jason L. Froebe, Ejason@froebe.netE
1341              
1342             =head1 COPYRIGHT AND LICENSE
1343              
1344             Copyright (C) 2009 by Jason L. Froebe
1345              
1346             This library is free software; you can redistribute it and/or modify
1347             it under the same terms as Perl itself, either Perl version 5.08.0 or,
1348             at your option, any later version of Perl 5 you may have available.
1349              
1350             =cut