source: branches/2.0/tools/piwigo_remote.pl @ 24636

Last change on this file since 24636 was 4345, checked in by plg, 15 years ago

feature 1051: ability to add/update a file for an existing photo. For example,
you can add the "high" later. Another example is to update the "web resized"
file (new dimensions is a common example). It also works for thumbnails.
Updating an existing file has no impact on the logical level (list of tags,
list of categories, title, description and so on).

  • Property svn:eol-style set to LF
File size: 10.2 KB
Line 
1#!/usr/bin/perl
2
3####
4# Usage examples
5#
6# time perl piwigo_remote.pl \
7#   --action=pwg.images.add \
8#   --file=erwann_rocher-web.jpg \
9#   --thumb=erwann_rocher-thumb.jpg \
10#   --high=erwann_rocher-high.jpg \
11#   --original=erwann_rocher-high.jpg \
12#   --define categories=9 \
13#   --chunk_size=200_000
14
15use strict;
16use warnings;
17
18use JSON;
19use LWP::UserAgent;
20# LWP::Debug::level('+');
21use Getopt::Long;
22use Encode qw/is_utf8 decode/;
23use POSIX qw(ceil floor);
24
25my %opt = ();
26GetOptions(
27    \%opt,
28    qw/
29          action=s
30          file=s
31          thumbnail=s
32          high=s
33          original=s
34          categories=s
35          chunk_size=i
36          base_url=s
37          username=s
38          password=s
39          define=s%
40      /
41);
42
43our $ua = LWP::UserAgent->new;
44$ua->agent('Mozilla/piwigo_remote.pl 1.25');
45$ua->cookie_jar({});
46
47my %conf;
48$conf{response_format} = 'json';
49$conf{limit} = 10;
50
51my %conf_default = (
52    base_url => 'http://localhost/piwigo/2.0',
53    username => 'plg',
54    password => 'plg',
55    chunk_size => 500_000,
56);
57foreach my $conf_key (keys %conf_default) {
58    $conf{$conf_key} = defined $opt{$conf_key} ? $opt{$conf_key} : $conf_default{$conf_key}
59}
60
61$ua->default_headers->authorization_basic(
62    $conf{username},
63    $conf{password}
64);
65
66my $result = undef;
67my $query = undef;
68
69binmode STDOUT, ":encoding(utf-8)";
70
71# TODO : don't connect at each script call, use the session duration instead.
72my $form = {
73    method => 'pwg.session.login',
74    username => $conf{username},
75    password => $conf{password},
76};
77
78$result = $ua->post(
79    $conf{base_url}.'/ws.php?format=json',
80    $form
81);
82
83# print "\n", $ua->cookie_jar->as_string, "\n";
84
85if ($opt{action} eq 'pwg.images.add') {
86    use Digest::MD5::File qw/file_md5_hex/;
87
88    $form = {};
89    $form->{method} = $opt{action};
90
91    my $original_sum = file_md5_hex($opt{original});
92    $form->{original_sum} = $original_sum;
93
94    send_chunks(
95        filepath => $opt{file},
96        type => 'file',
97        original_sum => $original_sum,
98    );
99    $form->{file_sum} = file_md5_hex($opt{file});
100
101    send_chunks(
102        filepath => $opt{thumbnail},
103        type => 'thumb',
104        original_sum => $original_sum,
105    );
106    $form->{thumbnail_sum} = file_md5_hex($opt{thumbnail});
107
108    if (defined $opt{high}) {
109        send_chunks(
110            filepath => $opt{high},
111            type => 'high',
112            original_sum => $original_sum,
113        );
114        $form->{high_sum} = file_md5_hex($opt{high});
115    }
116
117    foreach my $key (keys %{ $opt{define} }) {
118        $form->{$key} = $opt{define}{$key};
119    }
120
121    my $response = $ua->post(
122        $conf{base_url}.'/ws.php?format=json',
123        $form
124    );
125
126    print "-" x 50, "\n";
127    printf("response code    : %u\n", $response->code);
128    printf("response message : %s\n", $response->message);
129    print "-" x 50, "\n";
130    print "\n";
131
132#     use Data::Dumper;
133#     print Dumper($response->content);
134#     print Dumper(from_json($response->content));
135
136    if ($response->is_success) {
137        print "upload successful\n";
138    }
139    else {
140        print Dumper($response);
141        warn 'A problem has occured during upload', "\n";
142        warn $response->decoded_content, "\n";
143        die $response->status_line;
144    }
145}
146
147if ($opt{action} eq 'pwg.images.addFile') {
148    use Digest::MD5::File qw/file_md5_hex/;
149
150    if (not defined $opt{define}{image_id}) {
151        die '--define image_id=1234 is missing';
152    }
153
154    # which file type are we going to add/update?
155    my $type = undef;
156
157    foreach my $test_type (qw/thumbnail file high/) {
158        if (defined $opt{$test_type}) {
159            $type = $test_type;
160            last;
161        }
162    }
163
164    if (not defined $type) {
165        die 'at least one of file/thumbnail/high parameters must be set';
166    }
167
168    my $type_code = typecode_from_typename($type);
169
170    send_chunks(
171        filepath => $opt{$type},
172        type => $type_code,
173        original_sum => file_md5_hex($opt{original}),
174    );
175
176    $form = {};
177    $form->{method} = $opt{action};
178    $form->{type}   = $type_code;
179    $form->{sum}    = file_md5_hex($opt{$type});
180
181    foreach my $key (keys %{ $opt{define} }) {
182        $form->{$key} = $opt{define}{$key};
183    }
184
185    my $response = $ua->post(
186        $conf{base_url}.'/ws.php?format=json',
187        $form
188    );
189
190    print "-" x 50, "\n";
191    printf("response code    : %u\n", $response->code);
192    printf("response message : %s\n", $response->message);
193    print "-" x 50, "\n";
194    print "\n";
195
196#     use Data::Dumper;
197#     print Dumper($response->content);
198#     print Dumper(from_json($response->content));
199
200    if ($response->is_success) {
201        print "upload successful\n";
202    }
203    else {
204        print Dumper($response);
205        warn 'A problem has occured during upload', "\n";
206        warn $response->decoded_content, "\n";
207        die $response->status_line;
208    }
209}
210
211if ($opt{action} eq 'pwg.tags.list') {
212    use Text::ASCIITable;
213
214    $query = pwg_ws_get_query(
215        method => 'pwg.tags.getList',
216        sort_by_counter => 'true',
217    );
218
219    $result = $ua->get($query);
220    my $tag_result = from_json($result->content);
221    my $t = Text::ASCIITable->new({ headingText => 'Tags' });
222    $t->setCols('id','counter','name');
223
224    my $tag_number = 1;
225    foreach my $tag_href (@{ $tag_result->{result}{tags} }) {
226        $t->addRow(
227            $tag_href->{id},
228            $tag_href->{counter},
229            $tag_href->{name}
230        );
231
232        last if $tag_number++ >= $conf{limit};
233    }
234    print $t;
235}
236
237if ($opt{action} eq 'pwg.tags.getAdminList') {
238    $query = pwg_ws_get_query(
239        method => 'pwg.tags.getAdminList'
240    );
241
242    $result = $ua->get($query);
243    print Dumper($result);
244    my $tags = from_json($result->content)->{result}{tags};
245
246    foreach my $tag (@{$tags}) {
247        # print join(',', keys %{$tag}), "\n"; exit();
248        printf(
249            '{%u} %s ',
250            $tag->{id},
251            $tag->{name}
252        );
253    }
254
255    print "\n";
256}
257
258if ($opt{action} eq 'pwg.categories.add') {
259    $form = {
260        method => 'pwg.categories.add',
261        name => $opt{define}{name},
262        parent => $opt{define}{parent},
263    };
264
265    my $response = $ua->post(
266        $conf{base_url}.'/ws.php?format=json',
267        $form
268    );
269
270    use Data::Dumper;
271    print Dumper(from_json($response->content));
272}
273
274if ($opt{action} eq 'pwg.tags.add') {
275    $form = {
276        method => 'pwg.tags.add',
277        name => $opt{define}{name},
278    };
279
280    my $response = $ua->post(
281        $conf{base_url}.'/ws.php?format=json',
282        $form
283    );
284
285    use Data::Dumper;
286    print Dumper(from_json($response->content));
287}
288
289if ($opt{action} eq 'pwg.images.exist') {
290    $form = {
291        method => $opt{action},
292    };
293
294    foreach my $key (keys %{ $opt{define} }) {
295        $form->{$key} = $opt{define}{$key};
296    }
297
298    my $response = $ua->post(
299        $conf{base_url}.'/ws.php?format=json',
300        $form
301    );
302
303    use Data::Dumper;
304    print Dumper(from_json($response->content)->{result});
305    # print Dumper($response);
306}
307
308if ($opt{action} eq 'pwg.images.checkFiles') {
309    use Digest::MD5::File qw/file_md5_hex/;
310
311    $form = {};
312    $form->{method} = $opt{action};
313
314    foreach my $type (qw/thumbnail file high/) {
315        if (defined $opt{$type}) {
316            $form->{$type.'_sum'} = file_md5_hex($opt{$type});
317        }
318    }
319
320    foreach my $key (keys %{ $opt{define} }) {
321        $form->{$key} = $opt{define}{$key};
322    }
323
324    my $response = $ua->post(
325        $conf{base_url}.'/ws.php?format=json',
326        $form
327    );
328
329    print "-" x 50, "\n";
330    printf("response code    : %u\n", $response->code);
331    printf("response message : %s\n", $response->message);
332    print "-" x 50, "\n";
333    print "\n";
334
335    use Data::Dumper;
336    print Dumper(from_json($response->content));
337}
338
339if ($opt{action} eq 'pwg.images.setInfo' or $opt{action} eq 'pwg.categories.setInfo') {
340    $form = {
341        method => $opt{action},
342    };
343
344    foreach my $key (keys %{ $opt{define} }) {
345        $form->{$key} = $opt{define}{$key};
346    }
347
348    my $response = $ua->post(
349        $conf{base_url}.'/ws.php?format=json',
350        $form
351    );
352
353    use Data::Dumper;
354    # print Dumper(from_json($response->content)->{result});
355    print Dumper($response);
356}
357
358if ($opt{action} eq 'pwg.categories.getList') {
359    $form = {
360        method => $opt{action},
361    };
362
363    foreach my $key (keys %{ $opt{define} }) {
364        $form->{$key} = $opt{define}{$key};
365    }
366
367    my $response = $ua->post(
368        $conf{base_url}.'/ws.php?format=json',
369        $form
370    );
371
372    use Data::Dumper;
373    print Dumper($response->content);
374    print Dumper(from_json($response->content)->{result});
375    print Dumper($response);
376}
377
378
379$query = pwg_ws_get_query(
380    method => 'pwg.session.logout'
381);
382$ua->get($query);
383
384sub pwg_ws_get_query {
385    my %params = @_;
386
387    my $query = $conf{base_url}.'/ws.php?format='.$conf{response_format};
388
389    foreach my $key (keys %params) {
390        $query .= '&'.$key.'='.$params{$key};
391    }
392
393    return $query;
394}
395
396sub send_chunks {
397    my %params = @_;
398
399    use MIME::Base64 qw(encode_base64);
400    use File::Slurp;
401
402    my $content = read_file($params{filepath});
403    my $content_length = length($content);
404    my $nb_chunks = ceil($content_length / $conf{chunk_size});
405
406    my $chunk_pos = 0;
407    my $chunk_id = 1;
408    while ($chunk_pos < $content_length) {
409        my $chunk = substr(
410            $content,
411            $chunk_pos,
412            $conf{chunk_size}
413        );
414        $chunk_pos += $conf{chunk_size};
415
416        my $response = $ua->post(
417            $conf{base_url}.'/ws.php?format=json',
418            {
419                method => 'pwg.images.addChunk',
420                data => encode_base64($chunk),
421                original_sum => $params{original_sum},
422                position => $chunk_id,
423                type => $params{type},
424            }
425        );
426
427        printf(
428            'chunk %05u of %05u for %s "%s"'."\n",
429            $chunk_id,
430            $nb_chunks,
431            $params{type},
432            $params{filepath}
433        );
434        if ($response->code != 200) {
435            printf("response code    : %u\n", $response->code);
436            printf("response message : %s\n", $response->message);
437        }
438
439        $chunk_id++;
440    }
441}
442
443sub typecode_from_typename {
444    my ($typename) = @_;
445
446    my $typecode = $typename;
447
448    if ('thumbnail' eq $typename) {
449        $typecode = 'thumb';
450    }
451
452    return $typecode;
453}
Note: See TracBrowser for help on using the repository browser.