Flickr-Upload
view release on metacpan or search on metacpan
lib/Flickr/Upload.pm view on Meta::CPAN
$oauth{extra_params} = \%args;
$oauth{request_method} = 'POST';
$oauth{request_url} = $uri;
$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
my $req = Net::OAuth->request( "protected resource" )->new( %oauth );
$req->sign();
my $tmp_body = $req->to_post_body();
%args = ();
foreach (split '&', $tmp_body) {
my ($name, $val) = split '=', $_, 2;
$args{$name} = URI::Escape::uri_unescape( $val );
}
}
# unlikely that the caller would set up the photo as an array,
# but...
if( defined $photo ) {
$photo = [ $photo ] if ref $photo ne "ARRAY";
$args{photo} = $photo;
}
my $req = POST $uri, 'Content_Type' => 'form-data', 'Content' => \%args;
return $req;
}
=head2 upload_request
my $photoid = upload_request( $ua, $request );
Taking (at least) L<LWP::UserAgent> and L<HTTP::Request> objects as
arguments, this executes the request and processes the result as a
flickr upload. It's assumed that the request looks a lot like something
created with L<make_upload_request>. Note that the request must be signed
according to the Flickr API authentication rules.
Returns the resulting identifier of the uploaded photo (or ticket for
asynchronous uploads) on success, C<undef> on failure. According to the
API documentation, after an upload the user should be directed to the
page L<https://www.flickr.com/tools/uploader_edit.gne?ids=$photoid>.
=cut
sub upload_request {
my $self = shift;
die "$self is not a LWP::UserAgent" unless $self->isa('LWP::UserAgent');
my $req = shift;
die "expecting a HTTP::Request" unless $req->isa('HTTP::Request');
# Try 3 times to upload data. Without this flickr_upload is bound
# to die on large uploads due to some miscellaneous network
# issues. Timeouts on flickr or something else.
my ($res, $xml);
my $tries = 3;
for my $try (1 .. $tries) {
# Try to upload
$res = $self->request( $req );
return () unless defined $res;
if ($res->is_success) {
$xml = XMLin($res->decoded_content, KeyAttr=>[], ForceArray=>0);
return () unless defined $xml;
last;
} else {
my $what_next = ($try == $tries ? "giving up" : "trying again");
my $status = $res->status_line;
print STDERR "Failed uploading attempt attempt $try/$tries, $what_next. Message from server was: '$status'\n";
next;
}
}
my $photoid = $xml->{photoid};
my $ticketid = $xml->{ticketid};
unless( defined $photoid or defined $ticketid ) {
print STDERR "upload failed:\n", $res->decoded_content(), "\n";
return undef;
}
return (defined $photoid) ? $photoid : $ticketid;
}
=head2 file_length_in_encoded_chunk
$HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1;
my $photo = 'image.jpeg';
my $photo_size = (stat($photo))[7];
my $req = $ua->make_upload_request( ... );
my $gen = $req->content();
die unless ref($gen) eq "CODE";
my $state;
my $size;
$req->content(
sub {
my $chunk = &$gen();
$size += Flickr::Upload::file_length_in_encoded_chunk(\$chunk, \$state, $photo_size);
warn "$size bytes have now been uploaded";
return $chunk;
}
);
$rc = $ua->upload_request( $req );
This subroutine is tells you how much of a chunk in a series of
variable size multipart HTTP chunks contains a single file being
uploaded given a reference to the current chunk, a reference to a
state variable that lives between calls, and the size of the file
being uploaded.
It can be used used along with L<HTTP::Request::Common>'s
$HTTP::Request::Common::DYNAMIC_FILE_UPLOAD facility to implement
upload progress bars or other upload monitors, see L<flickr_upload>
for a practical example and F<t/progress_request.t> for tests.
=cut
sub file_length_in_encoded_chunk
{
my ($chunk, $s, $img_size) = @_;
$$s = {} unless ref $$s eq 'HASH';
# If we've run past the end of the image there's nothing to do but
# report no image content in this sector.
return 0 if $$s->{done};
unless ($$s->{in}) {
# Since we haven't found the image yet append this chunk to
# our internal data store, we do this because we have to do a
# regex match on m[Content-Type...] which might be split
# across multiple chunks
lib/Flickr/Upload.pm view on Meta::CPAN
# This chunk finishes the image
$$s->{done} = 1;
# Return what we had left
return $img_size - $$s->{size};
} else {
# This chunk isn't the last one
$$s->{size} += $size;
return $size;
}
}
}
=head2 photosets_create
Calls Flickr's "flickr.photosets.create" method,
to create a new Set.
The set will use the PrimaryPhotoID as the thumbnail photo.
returns: UNDEF on failure, PhotosetID on success.
my $photoset_id = $ua->photosets_create( title => 'title',
description => 'description',
primary_photo_id => ID,
auth_token => AUTH_TOKEN );
$ua->photosets_addphoto ( photoset_id => $photoset_id,
photo_id => ID );
=cut
sub photosets_create {
my $self = shift;
die '$self is not a Flickr::API' unless $self->isa('Flickr::API');
my %args = @_;
carp "Missing 'auth_token' parameter for photosets_create()"
unless exists $args{'auth_token'};
my $auth_token = $args{'auth_token'};
carp "Missing 'title' parameter for photosets_create()"
unless exists $args{'title'} && length($args{'title'})>0;
my $title = $args{'title'};
carp "Missing 'primary_photo_id' parameter for photosets_create()"
unless exists $args{'primary_photo_id'};
my $primary_photo_id = $args{'primary_photo_id'};
carp "Invalid primary_photo_id ($primary_photo_id) value (expecting numeric ID)" unless $primary_photo_id =~ /^[0-9]+$/;
my $description = ( exists $args{'description'} ) ? $args{'description'} : "" ;
my $res = $self->execute_method( 'flickr.photosets.create',
{ 'title' => $title,
'description' => $description,
'primary_photo_id' => $primary_photo_id,
'auth_token' => $auth_token,
} ) ;
#TODO: Add detailed error messages
return undef unless defined $res and $res->{success};
my $hash = XMLin($res->decoded_content(), KeyAttr=>[], ForceArray=>0);
my $photoset_id = $hash->{photoset}->{id};
if ( ! defined $photoset_id ) {
warn "Failed to extract photoset ID from response:\n" .
$res->decoded_content() . "\n\n";
return undef;
}
return $photoset_id ;
}
=head2 photosets_addphoto
Calls Flickr's "flickr.photosets.addPhoto" method,
to add a (existing) photo to an existing set.
returns: UNDEF on failure, TRUE on success.
my $photoset_id = $ua->photosets_create( title => 'title',
description => 'description',
primary_photo_id => ID,
auth_token => AUTH_TOKEN );
$ua->photosets_addphoto ( photoset_id => $photoset_id,
photo_id => ID );
=cut
sub photosets_addphoto {
my $self = shift;
die '$self is not a Flickr::API' unless $self->isa('Flickr::API');
my %args = @_;
carp "Missing 'auth_token' parameter for photosets_addphoto()"
unless exists $args{'auth_token'};
my $auth_token = $args{'auth_token'};
carp "Missing 'photoset_id' parameter for photosets_addphoto()"
unless exists $args{'photoset_id'};
my $photoset_id = $args{'photoset_id'};
carp "Missing 'photo_id' parameter for photosets_addphoto()"
unless exists $args{'photo_id'};
my $photo_id = $args{'photo_id'};
my $res = $self->execute_method( 'flickr.photosets.addPhoto',
{ 'photoset_id' => $photoset_id,
'photo_id' => $photo_id,
'auth_token' => $auth_token,
} ) ;
#TODO: Add detailed error messages
return undef unless defined $res;
return $res->{success};
}
# Private method adapted from Flickr::API
# See: https://www.flickr.com/services/api/auth.howto.web.html
sub _sign_args {
my $self = shift;
my $args = shift;
my $sig = $self->{api_secret};
for(sort { $a cmp $b } keys %$args) {
$sig .= $_ . (defined($args->{$_}) ? $args->{$_} : "");
}
return md5_hex($self->{unicode} ? encode_utf8($sig) : $sig);
( run in 0.421 second using v1.01-cache-2.11-cpan-39bf76dae61 )