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