# +-----------------------------------------------------------------------+ # | pLoader - a Perl photo uploader for Piwigo | # +-----------------------------------------------------------------------+ # | Copyright(C) 2008 Piwigo Team http://piwigo.org | # +-----------------------------------------------------------------------+ # | This program is free software; you can redistribute it and/or modify | # | it under the terms of the GNU General Public License as published by | # | the Free Software Foundation | # | | # | This program is distributed in the hope that it will be useful, but | # | WITHOUT ANY WARRANTY; without even the implied warranty of | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | # | General Public License for more details. | # | | # | You should have received a copy of the GNU General Public License | # | along with this program; if not, write to the Free Software | # | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | # | USA. | # +-----------------------------------------------------------------------+ package Uploader::PWG::WebServices; use strict; use warnings; use MIME::Base64 qw(encode_base64); use JSON; use LWP::UserAgent; use Data::Dumper; use Digest::MD5::File qw/file_md5_hex md5_hex/; use File::Slurp; use File::Spec; use POSIX qw(ceil floor); use base qw/ Uploader::Object Class::Accessor::Fast /; __PACKAGE__->mk_accessors( qw/ uagent urlbase username password qry_list_categories qry_add_categories qry_list_tags items tags categories site_high_file site_resized_file site_thumb_file site_image_name site_tags rank site_author site_comment site_img_date_creation uagent_response login_result action_result upload_high chunk_size sum_high_file sum_resized_file sum_thumb_file sum_original_file / ); $|=1; sub Init { my ( $self, $version ) = @_ ; $self->uagent( LWP::UserAgent->new( agent => sprintf("Mozilla/pLoader %s", $version) ) ); $self->uagent->cookie_jar({}); $self->urlbase( $self->{site_url} ); $self->username( $self->{site_username} ); $self->password( $self->{site_password} ); $self->chunk_size( $self->{chunk_size} ); $self->uagent->default_headers->authorization_basic( $self->{http_username}||$self->username, $self->{http_password}||$self->password ); $self->qry_list_categories( sprintf "%s/ws.php?format=json&method=%s&recursive=%s", $self->urlbase, # 'pwg.categories.getAdminList', 'pwg.categories.getList', 'true', ); $self->qry_list_tags( sprintf "%s/ws.php?format=json&method=%s", $self->urlbase, 'pwg.tags.getAdminList', ); my $form = { method => 'pwg.session.login', username => $self->username, password => $self->password, }; $self->uagent_response( $self->uagent->post( $self->urlbase.'/ws.php?format=json', $form ) ); my $hresult = {} ; if($self->uagent_response->is_success){ eval { $hresult = from_json( $self->uagent_response->content ); }; } else{ $hresult = { 'message' => $self->uagent_response->message, 'stat' => 'fail', }; } $self->login_result( $hresult ); } sub GetCategories { my ( $self ) = @_; my $result; eval { $result = $self->uagent->get( $self->qry_list_categories ); }; if($@){ printf("An error occured in query execution %s\n%s", $self->qry_list_categories, $@, ); } my $hresult; eval { $hresult = from_json( $result->content ); }; $hresult ||= {}; my $categories = $hresult->{result}{categories}; $categories; } sub GetTags { my ( $self ) = @_; my $result; eval { $result = $self->uagent->get( $self->qry_list_tags ); }; if($@){ printf("An error occured in query execution %s\n%s", $self->qry_list_tags, $@, ); } my $hresult; eval { $hresult = from_json( $result->content ); }; $hresult ||= {}; $hresult->{result}{tags}; } sub AddTags { my ( $self, $name ) = @_; my $form = { method => 'pwg.tags.add', name => $name, }; my $result = $self->uagent->post( $self->urlbase.'/ws.php?format=json', $form ); my $content = {}; eval { $content = from_json( $result->content ); }; return ( $result->is_success, $result->status_line, $content ); } sub UploadImage { my ( $self, $progress ) = @_; $self->sum_resized_file( $self->_checksum( $self->site_resized_file, $progress ) ); $self->sum_thumb_file( $self->_checksum( $self->site_thumb_file, $progress ) ); $self->sum_high_file( $self->_checksum( $self->site_high_file, $progress ) ); # is the same in pLoader but may be different $self->sum_original_file( $self->sum_high_file ); my $site_image_name = $self->site_image_name; my $image_id = $self->_exists($self->sum_original_file);; my $status = 0; my $status_line = "An error has occured during upload."; my $content = {}; my $doubleCheck; my $form; while(1){ if(!defined($image_id)){ my $resized_params = $self->_send_chunks('file', $progress); $status_line = $resized_params->{message}; last if !$resized_params->{ok}; my $thumb_params = $self->_send_chunks('thumb', $progress); $status_line = $thumb_params->{message}; last if !$thumb_params->{ok}; $form = { method => 'pwg.images.add', original_sum => $self->sum_original_file, file_sum => $self->sum_resized_file, thumbnail_sum => $self->sum_thumb_file, categories => $self->categories, name => $site_image_name, rank => $self->rank, author => $self->site_author, comment => $self->site_comment, date_creation => $self->site_img_date_creation, tag_ids => $self->site_tags, }; if($self->upload_high){ my $high_params = $self->_send_chunks('high', $progress); $status_line = $high_params->{message}; last if !$high_params->{ok}; # high is a HD resized copy of original $form->{high_sum} = $self->sum_high_file; } $doubleCheck = 1; } else { $form = { method => 'pwg.images.setInfo', image_id => $image_id, categories => $self->categories, name => $site_image_name, rank => $self->rank, author => $self->site_author, comment => $self->site_comment, date_creation => $self->site_img_date_creation, tag_ids => $self->site_tags, }; $progress->{yield}->(); } delete $form->{tag_ids} unless defined $self->site_tags; delete $form->{tag_ids} if '' eq $self->site_tags; $progress->{yield}->(); my $result = $self->uagent->post( $self->urlbase.'/ws.php?format=json', $form ); eval { $content = from_json( $result->content ); }; $status = $result->is_success; $status_line = $result->status_line; if($doubleCheck){ $content->{stat} = 'fail' if !$self->_exists($self->sum_original_file); } last; } return ( $status, $status_line, $content); } sub _exists { my ( $self, $md5_sum ) = @_; my $form = { method => 'pwg.images.exist', md5sum_list => $md5_sum, }; my $result = $self->uagent->post( $self->urlbase.'/ws.php?format=json', $form ); my $hresult = {}; eval { $hresult = from_json( $result->{_content} ); }; my $id; $id = $hresult->{result}{$self->sum_original_file} if 'ok' eq $hresult->{stat}; $id; } sub _checksum { my ( $self, $file, $progress ) = @_; my $file_sum; my $yield = $progress->{yield}; $yield->(); $progress->{msg_details}->( sprintf( "%s : %s", $progress->{checksum_msg}, $file ) ); eval { $file_sum = file_md5_hex( $file ); }; $yield->(); $file_sum; } sub _send_chunks { my ( $self, $type, $progress ) = @_; my $msg = { thumb=>'thumbnail_msg', file=>'resized_msg', high=>'highdef_msg', }; my $filepath = { thumb=>$self->site_thumb_file, file=>$self->site_resized_file, high=>$self->site_high_file, }; $progress->{current_msg} = $progress->{$msg->{$type}}; $progress->{yield}->(); my $params = { filepath => $filepath->{$type}, type => $type, original_sum => $self->sum_original_file, }; $self->send_chunks( $params, $progress, ); $progress->{yield}->(); $params; } sub AddCategories{ my ( $self, $name, $parentid ) = @_; my $form = { method => 'pwg.categories.add', name => $name, parent => $parentid, }; my $result = $self->uagent->post( $self->urlbase.'/ws.php?format=json', $form ); my $content = {}; eval { $content = from_json( $result->content ); }; return ( $result->is_success, $result->status_line, $content ); } sub SetInfoCategories{ my ( $self, $name, $comment, $parentid ) = @_; my $form = { method => 'pwg.categories.setInfo', name => $name, comment => $comment, category_id => $parentid, }; my $result = $self->uagent->post( $self->urlbase.'/ws.php?format=json', $form ); my $content = {}; eval { $content = from_json( $result->content ); }; return ( $result->is_success, $result->status_line, $content ); } sub send_chunks { my ( $self, $params, $progress ) = @_; my $yield = $progress->{yield}; my ( $vol, $dir, $filename ) = File::Spec->splitpath($params->{filepath}); $yield->(); $progress->{bar}->(0); $yield->(); $progress->{msg_details}->( sprintf( "%s : %s", $progress->{current_msg}, $filename ) ); $yield->(); my $content = read_file( $params->{filepath}, binmode => ':raw', ); $yield->(); my $content_length = length($content); my $nb_chunks = ceil($content_length / $self->chunk_size); my $chunk_pos = 0; my $chunk_id = 1; while ($chunk_pos < $content_length) { my $chunk = substr( $content, $chunk_pos, $self->chunk_size ); $chunk_pos += $self->chunk_size; my $response = $self->uagent->post( $self->urlbase.'/ws.php?format=json', { method => 'pwg.images.addChunk', data => encode_base64($chunk), original_sum => $params->{original_sum}, position => $chunk_id, type => $params->{type}, } ); $yield->(); $progress->{bar}->(100*($chunk_pos/$content_length)); $progress->{msg_details}->( sprintf( "%s : %s", $progress->{current_msg}, $filename ) ); $params->{ok} = 1; if ($response->code != 200) { printf("response code : %u\n", $response->code); printf("response message : %s\n", $response->message); $params->{ok} = 0; $params->{message} = $response->message; $params->{code} = $response->code; last; } $chunk_id++; } } 1;