source: extensions/pLoader/trunk/src/Uploader/PWG/WebServices.pm @ 4586

Last change on this file since 4586 was 4586, checked in by ronosman, 14 years ago

Bug fixed : only make high file checksum when needed.

  • Property svn:eol-style set to LF
File size: 16.7 KB
Line 
1# +-----------------------------------------------------------------------+
2# | pLoader - a Perl photo uploader for Piwigo                            |
3# +-----------------------------------------------------------------------+
4# | Copyright(C) 2008      Piwigo Team                  http://piwigo.org |
5# +-----------------------------------------------------------------------+
6# | This program is free software; you can redistribute it and/or modify  |
7# | it under the terms of the GNU General Public License as published by  |
8# | the Free Software Foundation                                          |
9# |                                                                       |
10# | This program is distributed in the hope that it will be useful, but   |
11# | WITHOUT ANY WARRANTY; without even the implied warranty of            |
12# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
13# | General Public License for more details.                              |
14# |                                                                       |
15# | You should have received a copy of the GNU General Public License     |
16# | along with this program; if not, write to the Free Software           |
17# | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
18# | USA.                                                                  |
19# +-----------------------------------------------------------------------+
20package Uploader::PWG::WebServices;
21 
22use strict;
23use warnings;
24use MIME::Base64 qw(encode_base64); 
25use JSON;
26use LWP::UserAgent;
27use Data::Dumper;
28use Digest::MD5::File qw/file_md5_hex md5_hex/;
29use File::Slurp;
30use File::Spec;
31use POSIX qw(ceil floor);
32use base qw/
33           Uploader::Object
34           Class::Accessor::Fast
35           /;
36
37__PACKAGE__->mk_accessors( 
38    qw/
39           uagent
40           urlbase
41           username
42           password
43           qry_list_categories
44           qry_add_categories
45           qry_list_tags
46           items
47           tags
48           categories
49           site_high_file
50           site_resized_file
51           site_thumb_file
52           site_image_name
53           site_tags
54           rank
55           site_author
56           site_comment
57           site_img_date_creation
58           uagent_response
59           login_result
60           action_result
61           upload_high
62           chunk_size
63           sum
64           image_id
65           typecode
66                   reupload_action_files
67                   reupload_action_properties
68                   reupload_action_properties_m
69                   single_value_mode
70                   multiple_value_mode
71                   privacy_level
72      / 
73);
74
75$|=1;
76
77sub Init {
78    my ( $self, $version ) = @_ ;
79
80    # to transform a type_code into a typename
81    $self->typecode(
82        {
83             file => 'file',
84             thumbnail => 'thumb',
85             high => 'high',   
86        }
87    );
88       
89        $self->single_value_mode(
90            {
91                    1 => 'fill_if_empty',
92                        2 => 'replace',
93                }
94        );
95
96        $self->multiple_value_mode(
97            {
98                    1 => 'append',
99                        2 => 'replace',
100                }
101        );
102       
103    $self->uagent(
104        LWP::UserAgent->new(
105            agent => sprintf("Mozilla/pLoader %s", $version)       
106        )
107    );
108   
109    $self->uagent->cookie_jar({});     
110
111    $self->urlbase(
112        $self->{site_url}
113    );
114   
115    $self->username(
116        $self->{site_username}
117    );
118   
119    $self->password(
120        $self->{site_password}
121    );
122   
123    $self->chunk_size(
124        $self->{chunk_size}
125    );
126
127   
128    $self->uagent->default_headers->authorization_basic(
129        $self->{http_username}||$self->username, 
130        $self->{http_password}||$self->password
131    );
132   
133   
134    $self->qry_list_categories( sprintf
135        "%s/ws.php?format=json&method=%s&recursive=%s",
136        $self->urlbase,
137#        'pwg.categories.getAdminList',
138        'pwg.categories.getList',
139        'true',
140    );
141
142    $self->qry_list_tags( sprintf
143        "%s/ws.php?format=json&method=%s",
144        $self->urlbase,
145        'pwg.tags.getAdminList',
146    );
147
148
149    my $form = {
150        method => 'pwg.session.login',
151        username => $self->username,
152        password => $self->password,
153    };
154 
155    $self->_execute_post(
156        $form
157    );   
158
159    $self->login_result(
160        $self->_json_response_content
161    );
162
163}
164
165sub _json_response_content {
166    my ( $self ) = @_;
167
168    my $hresult = {} ;
169
170    if($self->uagent_response->is_success){
171        # when server response has warnings, the content response is not a valid json string
172                # find the json response
173        $self->uagent_response->content =~ /(\{.+\})/;
174                #printf("json response %s\n", $1);
175        eval {
176            $hresult = from_json(
177                $1
178            );
179        };
180               
181    }
182    else{
183        $hresult = {
184            'message' => $self->uagent_response->message,
185            'stat'    => 'fail',
186        };
187    }
188       
189        $hresult;
190}
191
192sub _execute_get {
193    my ( $self, $query ) = @_;
194
195    eval {
196        $self->uagent_response(
197            $self->uagent->get(
198                $query
199                    )
200        );
201    };
202
203    if($@){
204        printf("An error occured in query execution %s\n%s",
205            $query,
206            $@,
207        );     
208    }
209}
210
211sub _execute_post {
212    my ( $self, $form ) = @_;
213
214    my $result;
215    eval {
216        $result = $self->uagent_response(
217            $self->uagent->post(
218                $self->urlbase.'/ws.php?format=json',
219                $form
220            )
221        );
222        };
223
224    if($@){
225        printf("An error occured in post execution %s\n%s",
226            $form->{method},
227            $@,
228        );     
229    }
230       
231        return ( $result->is_success, $result->status_line );
232}
233sub GetCategories {
234    my ( $self ) = @_;
235
236 
237    $self->_execute_get(
238            $self->qry_list_categories
239        );
240    $self->_json_response_content->{result}{categories};
241}
242
243sub GetTags {
244    my ( $self ) = @_;
245
246    $self->_execute_get(
247        $self->qry_list_tags   
248        );
249    $self->_json_response_content->{result}{tags};
250}
251
252sub AddTags {
253    my ( $self, $name ) = @_;
254
255    my $form = {
256        method            => 'pwg.tags.add',
257        name              => $name,
258       
259    };
260
261    return ( $self->_execute_post($form), $self->_json_response_content );
262} 
263
264
265sub UploadImage {
266    my ( $self, $progress ) = @_;
267
268    $self->image_id(
269        $self->_exists($progress->{original_sum})
270    );
271
272    my $status = 1;
273    my $status_line ="OK";
274    my $content = {};
275    my $doubleCheck;
276    my $form;
277    UPLOAD: while(1){
278        # first upload
279        if(!defined($self->image_id)){ 
280            $doubleCheck = 1;
281            $self->_checksum_files($progress);
282            my @types = ('file', 'thumb');
283            #printf("WS upload_high %s\n", $self->upload_high);
284            push @types, 'high' if $self->upload_high;
285            map{
286                my $rval = $self->_send_chunks($_, $progress);
287                $status_line = $rval->{message};
288                last UPLOAD if !$rval->{ok};
289            } @types;
290         
291       
292            $form = {
293                method            => 'pwg.images.add',
294                original_sum      => $self->sum->{original},
295                file_sum          => $self->sum->{file},
296                thumbnail_sum     => $self->sum->{thumb},
297                categories        => $self->categories,
298                name              => $self->site_image_name,
299                author            => $self->site_author,
300                comment           => $self->site_comment,
301                date_creation     => $self->site_img_date_creation,
302                tag_ids           => $self->site_tags,
303               
304            };
305
306           $form->{high_sum} = $self->sum->{high} if $self->upload_high;
307                   
308           $progress->{yield}->();
309           ( $status, $status_line ) = $self->_execute_post($form);
310                }
311        # re-upload
312        else{
313            # need to check if files have changed
314            # and update image info
315            if($self->reupload_action_files){
316                $self->_checksum_files($progress);
317                my $files = $self->_check_files();
318                if(defined($files)){
319                    $self->_add_files($files, $progress);       
320                }
321            }
322
323            $form = {
324                    method        => 'pwg.images.setInfo',
325                    image_id      => $self->image_id,
326                    };
327            # update metadata info
328            if($self->reupload_action_properties){     
329                        #printf("reupload_action_properties %s\n", $self->reupload_action_properties);
330                $form->{name}          = $self->site_image_name;
331                $form->{author}        = $self->site_author;
332                $form->{comment}       = $self->site_comment;
333                $form->{date_creation} = $self->site_img_date_creation;
334                                $form->{single_value_mode}  = $self->single_value_mode->{$self->reupload_action_properties};
335                $form->{level} = $self->privacy_level ? 2**($self->privacy_level - 1) : 0;
336                    }
337            if($self->reupload_action_properties_m){   
338                            #printf("reupload_action_properties_m %s\n", $self->reupload_action_properties_m);
339                $form->{tag_ids} = $self->site_tags if $self->site_tags;
340                $form->{categories} = $self->categories;
341                                $form->{multiple_value_mode} = $self->multiple_value_mode->{$self->reupload_action_properties_m};
342            };
343            $progress->{yield}->();
344            ( $status, $status_line ) = $self->_execute_post($form); 
345
346                }
347
348        delete $form->{tag_ids} unless defined $self->site_tags;
349        delete $form->{tag_ids} if '' eq $self->site_tags;
350
351        $progress->{yield}->();
352        # for first upload
353        if($doubleCheck){
354                    $self->image_id(
355                            $self->_exists($progress->{original_sum})
356                        );
357            $content->{stat} = !defined $self->image_id  ? 'fail' : 'ok'; 
358            if(defined $self->image_id and defined $self->privacy_level){
359                    ( $status, $status_line, $content ) = $self->_set_privacy_level;
360                }
361        }
362
363                last UPLOAD;
364    }# UPLOAD
365   
366    return ( $status,  $status_line, $content);
367}
368
369sub _set_privacy_level {
370    my ( $self ) = @_;
371               
372    my $form = {
373        method        => 'pwg.images.setPrivacyLevel',
374        image_id      => $self->image_id,
375        level         => $self->privacy_level ? 2**($self->privacy_level - 1) : 0,
376    };
377        print Dumper $form;
378        my ( $status, $status_line ) = $self->_execute_post($form); 
379    my $hresult = $self->_json_response_content;
380
381    ($status, $status_line, $hresult );
382}
383
384sub _checksum_files {
385    my ( $self, $progress ) = @_;
386
387    $self->sum(
388        {
389            file => $self->_checksum(
390                        $self->site_resized_file,
391                        $progress
392                    ),
393            thumb => $self->_checksum(
394                             $self->site_thumb_file,
395                             $progress
396                         ),
397            original => $progress->{original_sum}
398        }
399    ); 
400
401            high => $self->_checksum(
402                        $self->site_high_file,
403                        $progress
404                    ),
405
406    $self->sum->{high} = $self->_checksum(
407                        $self->site_high_file,
408                        $progress
409    ) if $self->upload_high ;
410}
411
412sub _check_files {
413    my ( $self ) = @_;
414
415    my $form = {
416        method   => 'pwg.images.checkFiles',
417        image_id => $self->image_id,
418    };
419   
420    @$form{'thumbnail_sum', 'file_sum' } = (
421        $self->sum->{thumb},
422        $self->sum->{file},
423    );
424
425    if($self->upload_high){
426        $form->{high_sum} = $self->sum->{high};
427    }
428
429    $self->_execute_post($form);   
430    my $hresult = $self->_json_response_content;
431
432    my $rval = 'ok' eq $hresult->{stat} ? $hresult->{result} : undef;
433   
434    $rval;
435}
436
437# $files is returned by _check_files
438# {
439#     thumbnail => 'equals', 'differs', 'missing'
440#     file => 'equals', 'differs', 'missing'
441#     high => 'equals', 'differs', 'missing'
442#}
443
444sub _add_files {
445    my ( $self, $files, $progress ) = @_;
446
447    map{
448        $self->_add_file($_, $progress);
449    }
450    map{
451        $self->typecode->{$_};
452    } grep { 'equals' ne $files->{$_} } keys %$files ;
453       
454}
455
456sub _add_file {
457    my ( $self, $type_code, $progress ) = @_;
458
459    $self->_send_chunks(
460        $type_code,
461        $progress,
462    );
463
464    my $form = {
465        method => 'pwg.images.addFile',
466        image_id => $self->image_id,
467        type     => $type_code,
468        sum      => $self->sum->{$type_code},           
469    }; 
470
471    $self->_execute_post($form);   
472    my $hresult = $self->_json_response_content;
473
474    my $rval = 'ok' eq $hresult->{stat} ? $hresult->{result} : undef;
475   
476    $rval;
477}
478
479sub IsAlreadyUploaded {
480    my ( $self, $md5_sums ) = @_;
481       
482        # md5_sums is an array ref
483        $self->_execute_post({
484        method      => 'pwg.images.exist',
485        md5sum_list => join(',', @$md5_sums)
486            }
487        );
488
489        my $sums = $self->_json_response_content->{result};
490
491    $sums;
492}
493
494sub _exists {
495    my ( $self, $md5_sum ) = @_;
496
497    my $form = {
498        method            => 'pwg.images.exist',
499        md5sum_list       => $md5_sum,
500    };
501
502    $self->_execute_post($form);   
503    my $hresult = $self->_json_response_content;
504
505        $hresult->{result} = {} if 'HASH' ne ref $hresult->{result};
506    my $id = 'ok' eq $hresult->{stat} ? $hresult->{result}{$md5_sum} : undef ;
507
508    $id;
509} 
510
511sub _checksum {
512    my ( $self, $file, $progress ) = @_;
513    my $file_sum;
514
515    my $yield = $progress->{yield};
516
517    $yield->();
518    $progress->{msg_details}->(
519        sprintf(
520            "%s : %s", 
521            $progress->{checksum_msg}, 
522            $file
523        )
524    );
525    eval {
526        $file_sum = file_md5_hex(
527            $file
528        );
529    };
530    $yield->();
531
532    $file_sum;
533}
534
535sub _send_chunks {
536    my ( $self, $type_code, $progress ) = @_;
537
538    my $msg = {
539        thumb=>'thumbnail_msg',
540        file=>'resized_msg',
541        high=>'highdef_msg',
542    };
543
544    my $filepath = {
545        thumb=>$self->site_thumb_file,
546        file=>$self->site_resized_file,
547        high=>$self->site_high_file,
548    };
549
550    $progress->{current_msg} = $progress->{$msg->{$type_code}};
551    $progress->{yield}->();
552
553    my $params = {
554         filepath      => $filepath->{$type_code},
555         type          => $type_code,
556         original_sum  => $self->sum->{original},
557    };
558    #print Dumper $params;
559    $self->send_chunks(
560       $params,
561       $progress,
562    );
563    $progress->{yield}->();
564
565    $params;
566}
567
568sub AddCategories{
569    my ( $self, $name, $parentid ) = @_;
570
571    my $form = {
572        method            => 'pwg.categories.add',
573        name              => $name,
574        parent            => $parentid,
575       
576    };
577       
578    return ( $self->_execute_post($form), $self->_json_response_content );
579}
580
581sub SetInfoCategories{
582    my ( $self, $name, $comment, $parentid ) = @_;
583
584    my $form = {
585        method            => 'pwg.categories.setInfo',
586        name              => $name,
587        comment           => $comment,
588        category_id       => $parentid,
589       
590    };
591
592    return ( $self->_execute_post($form), $self->_json_response_content );
593}
594
595
596sub send_chunks {
597    my ( $self, $params, $progress ) = @_;
598
599    my $yield = $progress->{yield};
600    my ( $vol, $dir, $filename ) = File::Spec->splitpath($params->{filepath});
601
602    $yield->();
603    $progress->{bar}->(0);
604    $yield->();
605    $progress->{msg_details}->(
606        sprintf(
607            "%s : %s", 
608            $progress->{current_msg}, 
609            $filename
610        )
611    );
612
613
614    $yield->();
615    my $content = read_file(
616        $params->{filepath},
617        binmode => ':raw',
618    );
619    $yield->();
620
621    my $content_length = length($content);
622    my $nb_chunks = ceil($content_length / $self->chunk_size);
623
624    my $chunk_pos = 0;
625    my $chunk_id = 1;
626
627
628    while ($chunk_pos < $content_length) {
629
630        my $chunk = substr(
631            $content,
632            $chunk_pos,
633            $self->chunk_size
634        );
635        $chunk_pos += $self->chunk_size;
636        my $response = $self->uagent->post(
637            $self->urlbase.'/ws.php?format=json',
638            {
639                method       => 'pwg.images.addChunk',
640                data         => encode_base64($chunk),
641                original_sum => $params->{original_sum},
642                position     => $chunk_id,
643                type         => $params->{type},
644            }
645        );
646        $yield->();
647        $progress->{bar}->(100*($chunk_pos/$content_length));
648        $progress->{msg_details}->(
649            sprintf(
650                "%s : %s", 
651                $progress->{current_msg}, 
652                $filename
653            )
654        );
655        $params->{ok} = 1;
656        if ($response->code != 200) {
657            printf("response code    : %u\n", $response->code);
658            printf("response message : %s\n", $response->message);
659            $params->{ok} = 0;
660            $params->{message} = $response->message;
661            $params->{code} = $response->code;
662            last;
663        }
664
665        $chunk_id++;
666    }
667}
668
669 
6701;
671   
Note: See TracBrowser for help on using the repository browser.