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

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

Feature 1320 added : add privacy level in photo properties.

  • Property svn:eol-style set to LF
File size: 16.4 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            push @types, 'high' if $self->upload_high;
284            map{
285                my $rval = $self->_send_chunks($_, $progress);
286                $status_line = $rval->{message};
287                last UPLOAD if !$rval->{ok};
288            } @types;
289         
290       
291            $form = {
292                method            => 'pwg.images.add',
293                original_sum      => $self->sum->{original},
294                file_sum          => $self->sum->{file},
295                thumbnail_sum     => $self->sum->{thumb},
296                categories        => $self->categories,
297                name              => $self->site_image_name,
298                author            => $self->site_author,
299                comment           => $self->site_comment,
300                date_creation     => $self->site_img_date_creation,
301                tag_ids           => $self->site_tags,
302               
303            };
304
305           $form->{high_sum} = $self->sum->{high} if $self->upload_high;
306                   
307           $progress->{yield}->();
308           ( $status, $status_line ) = $self->_execute_post($form);
309                }
310        # re-upload
311        else{
312            # need to check if files have changed
313            # and update image info
314            if($self->reupload_action_files){
315                $self->_checksum_files($progress);
316                my $files = $self->_check_files();
317                if(defined($files)){
318                    $self->_add_files($files, $progress);       
319                }
320            }
321
322            $form = {
323                    method        => 'pwg.images.setInfo',
324                    image_id      => $self->image_id,
325                    };
326            # update metadata info
327            if($self->reupload_action_properties){     
328                        #printf("reupload_action_properties %s\n", $self->reupload_action_properties);
329                $form->{name}          = $self->site_image_name;
330                $form->{author}        = $self->site_author;
331                $form->{comment}       = $self->site_comment;
332                $form->{date_creation} = $self->site_img_date_creation;
333                                $form->{single_value_mode}  = $self->single_value_mode->{$self->reupload_action_properties};
334                $form->{level} = $self->privacy_level ? 2**($self->privacy_level - 1) : 0;
335                    }
336            if($self->reupload_action_properties_m){   
337                            #printf("reupload_action_properties_m %s\n", $self->reupload_action_properties_m);
338                $form->{tag_ids} = $self->site_tags if $self->site_tags;
339                $form->{categories} = $self->categories;
340                                $form->{multiple_value_mode} = $self->multiple_value_mode->{$self->reupload_action_properties_m};
341            };
342            $progress->{yield}->();
343            ( $status, $status_line ) = $self->_execute_post($form); 
344
345                }
346
347        delete $form->{tag_ids} unless defined $self->site_tags;
348        delete $form->{tag_ids} if '' eq $self->site_tags;
349
350        $progress->{yield}->();
351        # for first upload
352        if($doubleCheck){
353                    $self->image_id(
354                            $self->_exists($progress->{original_sum})
355                        );
356            $content->{stat} = !defined $self->image_id  ? 'fail' : 'ok'; 
357            if(defined $self->image_id and defined $self->privacy_level){
358                    ( $status, $status_line, $content ) = $self->_set_privacy_level;
359                }
360        }
361
362                last UPLOAD;
363    }# UPLOAD
364   
365    return ( $status,  $status_line, $content);
366}
367
368sub _set_privacy_level {
369    my ( $self ) = @_;
370               
371    my $form = {
372        method        => 'pwg.images.setPrivacyLevel',
373        image_id      => $self->image_id,
374        level         => $self->privacy_level ? 2**($self->privacy_level - 1) : 0,
375    };
376        print Dumper $form;
377        my ( $status, $status_line ) = $self->_execute_post($form); 
378    my $hresult = $self->_json_response_content;
379
380    ($status, $status_line, $hresult );
381}
382
383sub _checksum_files {
384    my ( $self, $progress ) = @_;
385
386    $self->sum(
387        {
388            file => $self->_checksum(
389                        $self->site_resized_file,
390                        $progress
391                    ),
392            thumb => $self->_checksum(
393                             $self->site_thumb_file,
394                             $progress
395                         ),
396            high => $self->_checksum(
397                        $self->site_high_file,
398                        $progress
399                    ),
400            original => $progress->{original_sum}
401        }
402    ); 
403}
404
405sub _check_files {
406    my ( $self ) = @_;
407
408    my $form = {
409        method   => 'pwg.images.checkFiles',
410        image_id => $self->image_id,
411    };
412   
413    @$form{'thumbnail_sum', 'file_sum' } = (
414        $self->sum->{thumb},
415        $self->sum->{file},
416    );
417
418    if($self->upload_high){
419        $form->{high_sum} = $self->sum->{high};
420    }
421
422    $self->_execute_post($form);   
423    my $hresult = $self->_json_response_content;
424
425    my $rval = 'ok' eq $hresult->{stat} ? $hresult->{result} : undef;
426   
427    $rval;
428}
429
430# $files is returned by _check_files
431# {
432#     thumbnail => 'equals', 'differs', 'missing'
433#     file => 'equals', 'differs', 'missing'
434#     high => 'equals', 'differs', 'missing'
435#}
436
437sub _add_files {
438    my ( $self, $files, $progress ) = @_;
439
440    map{
441        $self->_add_file($_, $progress);
442    }
443    map{
444        $self->typecode->{$_};
445    } grep { 'equals' ne $files->{$_} } keys %$files ;
446       
447}
448
449sub _add_file {
450    my ( $self, $type_code, $progress ) = @_;
451
452    $self->_send_chunks(
453        $type_code,
454        $progress,
455    );
456
457    my $form = {
458        method => 'pwg.images.addFile',
459        image_id => $self->image_id,
460        type     => $type_code,
461        sum      => $self->sum->{$type_code},           
462    }; 
463
464    $self->_execute_post($form);   
465    my $hresult = $self->_json_response_content;
466
467    my $rval = 'ok' eq $hresult->{stat} ? $hresult->{result} : undef;
468   
469    $rval;
470}
471
472sub IsAlreadyUploaded {
473    my ( $self, $md5_sums ) = @_;
474       
475        # md5_sums is an array ref
476        $self->_execute_post({
477        method      => 'pwg.images.exist',
478        md5sum_list => join(',', @$md5_sums)
479            }
480        );
481
482        my $sums = $self->_json_response_content->{result};
483
484    $sums;
485}
486
487sub _exists {
488    my ( $self, $md5_sum ) = @_;
489
490    my $form = {
491        method            => 'pwg.images.exist',
492        md5sum_list       => $md5_sum,
493    };
494
495    $self->_execute_post($form);   
496    my $hresult = $self->_json_response_content;
497
498        $hresult->{result} = {} if 'HASH' ne ref $hresult->{result};
499    my $id = 'ok' eq $hresult->{stat} ? $hresult->{result}{$md5_sum} : undef ;
500
501    $id;
502} 
503
504sub _checksum {
505    my ( $self, $file, $progress ) = @_;
506    my $file_sum;
507
508    my $yield = $progress->{yield};
509
510    $yield->();
511    $progress->{msg_details}->(
512        sprintf(
513            "%s : %s", 
514            $progress->{checksum_msg}, 
515            $file
516        )
517    );
518    eval {
519        $file_sum = file_md5_hex(
520            $file
521        );
522    };
523    $yield->();
524
525    $file_sum;
526}
527
528sub _send_chunks {
529    my ( $self, $type_code, $progress ) = @_;
530
531    my $msg = {
532        thumb=>'thumbnail_msg',
533        file=>'resized_msg',
534        high=>'highdef_msg',
535    };
536
537    my $filepath = {
538        thumb=>$self->site_thumb_file,
539        file=>$self->site_resized_file,
540        high=>$self->site_high_file,
541    };
542
543    $progress->{current_msg} = $progress->{$msg->{$type_code}};
544    $progress->{yield}->();
545
546    my $params = {
547         filepath      => $filepath->{$type_code},
548         type          => $type_code,
549         original_sum  => $self->sum->{original},
550    };
551print Dumper $params;
552    $self->send_chunks(
553       $params,
554       $progress,
555    );
556    $progress->{yield}->();
557
558    $params;
559}
560
561sub AddCategories{
562    my ( $self, $name, $parentid ) = @_;
563
564    my $form = {
565        method            => 'pwg.categories.add',
566        name              => $name,
567        parent            => $parentid,
568       
569    };
570       
571    return ( $self->_execute_post($form), $self->_json_response_content );
572}
573
574sub SetInfoCategories{
575    my ( $self, $name, $comment, $parentid ) = @_;
576
577    my $form = {
578        method            => 'pwg.categories.setInfo',
579        name              => $name,
580        comment           => $comment,
581        category_id       => $parentid,
582       
583    };
584
585    return ( $self->_execute_post($form), $self->_json_response_content );
586}
587
588
589sub send_chunks {
590    my ( $self, $params, $progress ) = @_;
591
592    my $yield = $progress->{yield};
593    my ( $vol, $dir, $filename ) = File::Spec->splitpath($params->{filepath});
594
595    $yield->();
596    $progress->{bar}->(0);
597    $yield->();
598    $progress->{msg_details}->(
599        sprintf(
600            "%s : %s", 
601            $progress->{current_msg}, 
602            $filename
603        )
604    );
605
606
607    $yield->();
608    my $content = read_file(
609        $params->{filepath},
610        binmode => ':raw',
611    );
612    $yield->();
613
614    my $content_length = length($content);
615    my $nb_chunks = ceil($content_length / $self->chunk_size);
616
617    my $chunk_pos = 0;
618    my $chunk_id = 1;
619
620
621    while ($chunk_pos < $content_length) {
622
623        my $chunk = substr(
624            $content,
625            $chunk_pos,
626            $self->chunk_size
627        );
628        $chunk_pos += $self->chunk_size;
629        my $response = $self->uagent->post(
630            $self->urlbase.'/ws.php?format=json',
631            {
632                method       => 'pwg.images.addChunk',
633                data         => encode_base64($chunk),
634                original_sum => $params->{original_sum},
635                position     => $chunk_id,
636                type         => $params->{type},
637            }
638        );
639        $yield->();
640        $progress->{bar}->(100*($chunk_pos/$content_length));
641        $progress->{msg_details}->(
642            sprintf(
643                "%s : %s", 
644                $progress->{current_msg}, 
645                $filename
646            )
647        );
648        $params->{ok} = 1;
649        if ($response->code != 200) {
650            printf("response code    : %u\n", $response->code);
651            printf("response message : %s\n", $response->message);
652            $params->{ok} = 0;
653            $params->{message} = $response->message;
654            $params->{code} = $response->code;
655            last;
656        }
657
658        $chunk_id++;
659    }
660}
661
662 
6631;
664   
Note: See TracBrowser for help on using the repository browser.