source: extensions/piwigo_import_tree/piwigo_import_tree.pl @ 18577

Last change on this file since 18577 was 18577, checked in by plg, 12 years ago

add a local cache system, to avoid problem of photo/album name matching with accents

File size: 9.4 KB
Line 
1#!/usr/bin/perl
2
3# usage:
4# perl piwigo_import_tree.pl --directory=/Users/pierrick/piwigo/album1
5
6use strict;
7use warnings;
8
9# make it compatible with Windows, but breaks Linux
10#use utf8;
11
12use File::Find;
13use Data::Dumper;
14use File::Basename;
15use LWP::UserAgent;
16use JSON;
17use Getopt::Long;
18use Encode qw/is_utf8 decode/;
19use Time::HiRes qw/gettimeofday tv_interval/;
20use Digest::MD5 qw/md5 md5_hex/;
21
22my %opt = ();
23GetOptions(
24    \%opt,
25    qw/
26          base_url=s
27          username=s
28          password=s
29          directory=s
30          parent_album_id=s
31          define=s%
32          quiet
33          only_write_cache
34      /
35);
36
37my $album_dir = $opt{directory};
38$album_dir =~ s{^\./*}{};
39
40our $ua = LWP::UserAgent->new;
41$ua->agent('Mozilla/piwigo_remote.pl 1.25');
42$ua->cookie_jar({});
43
44my %conf;
45my %conf_default = (
46    base_url => 'http://localhost/plg/piwigo/salon',
47    username => 'plg',
48    password => 'plg',
49);
50
51foreach my $conf_key (keys %conf_default) {
52    $conf{$conf_key} = defined $opt{$conf_key} ? $opt{$conf_key} : $conf_default{$conf_key}
53}
54
55$ua->default_headers->authorization_basic(
56    $conf{username},
57    $conf{password}
58);
59
60my $result = undef;
61my $query = undef;
62
63binmode STDOUT, ":encoding(utf-8)";
64
65# Login to Piwigo
66piwigo_login();
67
68# Fill an "album path to album id" cache
69my %piwigo_albums = ();
70
71my $response = $ua->post(
72    $conf{base_url}.'/ws.php?format=json',
73    {
74        method => 'pwg.categories.getList',
75        recursive => 1,
76        fullname => 1,
77    }
78);
79
80my $albums_aref = from_json($response->content)->{result}->{categories};
81foreach my $album_href (@{$albums_aref}) {
82    $piwigo_albums{ $album_href->{name} } = $album_href->{id};
83}
84# print Dumper(\%piwigo_albums)."\n\n";
85
86if (defined $opt{parent_album_id}) {
87    foreach my $album_path (keys %piwigo_albums) {
88        if ($piwigo_albums{$album_path} == $opt{parent_album_id}) {
89            $conf{parent_album_id} = $opt{parent_album_id};
90            $conf{parent_album_path} = $album_path;
91        }
92    }
93
94    if (not defined $conf{parent_album_path}) {
95        print "Parent album ".$opt{parent_album_id}." does not exist\n";
96        exit();
97    }
98}
99
100# Initialize a cache with file names of existing photos, for related albums
101my %photos_of_album = ();
102
103# Synchronize local folder with remote Piwigo gallery
104find({wanted => \&add_to_piwigo, no_chdir => 1}, $album_dir);
105
106#---------------------------------------------------------------------
107# Functions
108#---------------------------------------------------------------------
109
110sub piwigo_login {
111    $ua->post(
112        $conf{base_url}.'/ws.php?format=json',
113        {
114            method => 'pwg.session.login',
115            username => $conf{username},
116            password => $conf{password},
117        }
118    );
119}
120
121sub fill_photos_of_album {
122    my %params = @_;
123
124    if (defined $photos_of_album{ $params{album_id} }) {
125        return 1;
126    }
127
128    piwigo_login();
129    my $response = $ua->post(
130        $conf{base_url}.'/ws.php?format=json',
131        {
132            method => 'pwg.categories.getImages',
133            cat_id => $params{album_id},
134            per_page => 1000000,
135        }
136    );
137
138    foreach my $image_href (@{from_json($response->content)->{result}{images}{_content}}) {
139        $photos_of_album{ $params{album_id} }{ $image_href->{file} } = 1;
140    }
141}
142
143sub photo_exists {
144    my %params = @_;
145
146    fill_photos_of_album(album_id => $params{album_id});
147
148    if (defined $photos_of_album{ $params{album_id} }{ $params{file} }) {
149        return 1;
150    }
151    else {
152        return 0;
153    }
154}
155
156sub add_album {
157    my %params = @_;
158
159    my $form = {
160        method => 'pwg.categories.add',
161        name => $params{name},
162    };
163
164    if (defined $params{parent}) {
165        $form->{parent} = $params{parent};
166    }
167
168    piwigo_login();
169    my $response = $ua->post(
170        $conf{base_url}.'/ws.php?format=json',
171        $form
172    );
173
174    return from_json($response->content)->{result}{id};
175}
176
177sub add_photo {
178    my %params = @_;
179
180    my $form = {
181        method => 'pwg.images.addSimple',
182        image => [$params{path}],
183        category => $params{album_id},
184    };
185
186    # is there any title defined in a descript.ion file?
187    my $property = undef;
188    my $desc_filepath = dirname($params{path}).'/descript.ion';
189    if (-f $desc_filepath) {
190        my $photo_filename = basename($params{path});
191        open(IN, '<', $desc_filepath);
192        while (my $desc_line = <IN>) {
193            if ($desc_line =~ /^$photo_filename/) {
194                chomp($desc_line);
195                $property = (split /\t/, $desc_line, 2)[1];
196            }
197        }
198        close(IN);
199    }
200
201    if (defined $property and $property ne '') {
202        $form->{name} = $property;
203    }
204
205    print '[album '.$params{album_id}.'] '.$params{path}.' upload starts... ';
206    $| = 1;
207    my $t1 = [gettimeofday];
208
209    piwigo_login();
210    my $response = $ua->post(
211        $conf{base_url}.'/ws.php?format=json',
212        $form,
213        'Content_Type' => 'form-data',
214    );
215
216    my $elapsed = tv_interval($t1);
217    print ' upload completed ('.sprintf('%u ms', $elapsed * 1000).')'."\n";
218}
219
220sub add_to_piwigo {
221    # print $File::Find::name."\n";
222    my $path = $File::Find::name;
223    my $parent_dir = dirname($album_dir);
224    if ($parent_dir ne '.') {
225        # print '$parent_dir = '.$parent_dir."\n";
226        $path =~ s{^$parent_dir/}{};
227    }
228    # print $path."\n";
229
230    if (-d) {
231        my $up_dir = '';
232        my $parent_id = undef;
233
234        if (defined $conf{parent_album_path}) {
235            $up_dir = $conf{parent_album_path}.' / ';
236            $parent_id = $conf{parent_album_id};
237        }
238
239        foreach my $dir (split '/', $path) {
240            if (not defined $piwigo_albums{$up_dir.$dir}) {
241                my $id = cached_album(dir => $up_dir.$dir);
242                # if the album is not in the cache OR if the id in the cache
243                # matches no album fetched by pwg.categories.getList, then
244                # we have to create the album first
245                if (not defined $id or not grep($_ eq $id, values(%piwigo_albums))) {
246                    print 'album "'.$up_dir.$dir.'" must be created'."\n";
247                    $id = add_album(name => $dir, parent => $parent_id);
248                    cache_add_album(dir => $up_dir.$dir, id => $id);
249                }
250                $piwigo_albums{$up_dir.$dir} = $id;
251            }
252            $parent_id = $piwigo_albums{$up_dir.$dir};
253            $up_dir.= $dir.' / ';
254        }
255    }
256
257    if (-f and $path =~ /\.(jpe?g|gif|png)$/i) {
258        my $album_key = join(' / ', split('/', dirname($path)));
259
260        if (defined $conf{parent_album_path}) {
261            $album_key = $conf{parent_album_path}.' / '.$album_key;
262        }
263
264        my $album_id = $piwigo_albums{$album_key};
265
266        if (photo_exists(album_id => $album_id, file => basename($File::Find::name))) {
267            if (not $opt{quiet}) {
268                print $File::Find::name.' already exists in Piwigo, skipped'."\n";
269            }
270            return 1;
271        }
272
273        if (cached_photo(path => $File::Find::name, dir => $album_key)) {
274            return 1;
275        }
276
277        if ($opt{only_write_cache}) {
278            cache_add_photo(path => $File::Find::name, dir => $album_key);
279            return 1;
280        }
281
282        add_photo(path => $File::Find::name, album_id => $album_id);
283        cache_add_photo(path => $File::Find::name, dir => $album_key);
284        # print 'dirname = '.dirname($path)."\n\n";
285    }
286}
287
288sub cache_add_photo {
289    my %params = @_;
290
291    if (cached_photo(path => $params{path}, dir => $params{dir})) {
292        if (not $opt{quiet}) {
293            print 'photo is in the cache, no upload'."\n";
294        }
295        return 1;
296    }
297
298    $params{dir} =~ s{ / }{/}g;
299
300    my $filepath = $params{dir}.'/.piwigo_import_tree.txt';
301
302    open(my $ofh, '>> '.$filepath) or die 'cannot open file "'.$filepath.'" for writing';
303    print {$ofh} $conf{base_url}.' '.md5_hex(basename($params{path}));
304
305    if (defined $params{id}) {
306        print {$ofh} ' [id='.$params{id}.']';
307    }
308
309    print {$ofh} "\n";
310    close($ofh);
311}
312
313sub cached_photo {
314    my %params = @_;
315
316    $params{dir} =~ s{ / }{/}g;
317
318    my $filepath = $params{dir}.'/.piwigo_import_tree.txt';
319
320    if (not -f $filepath) {
321        return undef;
322    }
323
324    my $photo_id = undef;
325    my $photo_filename_md5 = md5_hex(basename($params{path}));
326
327    open(my $ifh, '<'.$filepath) or die 'cannot open file "'.$filepath.'" for reading';
328    while (my $line = <$ifh>) {
329        chomp $line;
330        if ($line =~ m/$photo_filename_md5/) {
331            # TODO if needed : search the [id=(\d+)] for photo_id
332            return 1;
333        }
334    }
335    close($ifh);
336
337    return undef;
338}
339
340sub cache_add_album {
341    my %params = @_;
342
343    $params{dir} =~ s{ / }{/}g;
344
345    my $filepath = $params{dir}.'/.piwigo_import_tree.txt';
346
347    open(my $ofh, '>> '.$filepath) or die 'cannot open file "'.$filepath.'" for writing';
348    print {$ofh} $conf{base_url}.' album_id = '.$params{id}."\n";
349    print $conf{base_url}.' album_id = '.$params{id}."\n";
350    close($ofh);
351}
352
353sub cached_album {
354    my %params = @_;
355
356    $params{dir} =~ s{ / }{/}g;
357
358    my $filepath = $params{dir}.'/.piwigo_import_tree.txt';
359
360    if (not -f $filepath) {
361        return undef;
362    }
363
364    my $album_id = undef;
365
366    open(my $ifh, '<'.$filepath) or die 'cannot open file "'.$filepath.'" for reading';
367    while (my $line = <$ifh>) {
368        chomp $line;
369        if ($line =~ m/album_id = (\d+)/) {
370            $album_id = $1;
371        }
372    }
373    close($ifh);
374
375    print 'directory "'.$params{dir}.'" was found as album '.$album_id."\n";
376
377    return $album_id;
378}
Note: See TracBrowser for help on using the repository browser.