source: extensions/pLoader/trunk/src/Uploader/Images.pm @ 6738

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

Add a notebook to display "Preparation" and "Transfer" panels. The transfer progress list is no longer displayed as a dialog box. The photos are removed from the preparation panel when transfer begins.

File size: 13.4 KB
Line 
1package Uploader::Images;
2use strict;
3use base qw/Uploader::Object/;
4use Uploader::Image;
5use Digest::MD5::File qw/file_md5_hex md5_hex/;
6require Win32 if($^O =~ /MSWin32/);
7use Storable;
8use Data::Dumper;
9
10$|=1;
11
12my @cbk_properties = qw/
13    add_images_cbk
14    delete_images_cbk
15    create_wx_thumbnail_cbk
16    default_caption_cbk
17    default_caption_pattern_cbk
18/;
19
20my @storable_properties = qw/
21    ordered_image_ids
22    selection
23    _images
24    current_image
25    selection_tags
26    selection_privacy_level
27    selection_name
28    selection_comment
29    selection_author
30    selection_create_date
31    author
32    type
33    global_rank
34    global_rank_indx
35/;
36
37my @app_properties = qw/
38    eng_caption_patterns
39/;
40
41my @tmp_properties = qw/
42    to_be_removed_grank
43/;
44
45
46__PACKAGE__->mk_accessors(@cbk_properties);
47__PACKAGE__->mk_accessors(@storable_properties);
48__PACKAGE__->mk_accessors(@app_properties);
49__PACKAGE__->mk_accessors(@tmp_properties);
50
51
52sub Init {
53    my ( $self ) = @_;
54
55    $self->_images({}) if !defined $self->_images;
56    $self->ordered_image_ids([]) if !defined $self->ordered_image_ids;
57    $self->selection([]) if !defined $self->selection;
58    $self->global_rank(-1) if !defined $self->global_rank;
59    $self->global_rank_indx({}) if !defined $self->global_rank_indx;
60    $self->to_be_removed_grank([]) if !defined $self->to_be_removed_grank;
61}
62
63
64sub add_images {
65    my ( $self, $file_paths ) = @_;
66
67    my $files = [
68        map {
69            # to make sure that unicode chars in filenames are supported
70            {
71                ANSIPathName => $^O =~ /MSWin32/ ? Win32::GetANSIPathName($_) : $_,
72                PathName => $_,
73            },
74        }@$file_paths
75    ];
76
77    @$files = sort { $a->{PathName} cmp $b->{PathName} } @$files;   
78
79
80
81    foreach my $file ( @{$files} ) {
82        # to append at the end of list
83        $self->add_image_from_file($file);
84    }
85   
86    $self->Store;
87   
88}
89
90
91sub add_image_from_file {
92    my ( $self, $file ) = @_;   
93
94    # pLoader image id
95    my $image_id = $self->new_image_id(
96        $file->{ANSIPathName}
97    );
98
99    my $i = $self->image_count;
100    $self->ordered_image_ids->[$i] = $image_id ;
101
102    $self->global_rank(
103        1+$self->global_rank
104    );
105
106    $self->set_to_be_removed(
107        $self->global_rank
108    );
109
110    $self->global_rank_indx->{
111        $self->global_rank
112    } = $i;
113
114    my $image = $self->_images->{
115        $image_id
116    };
117    if ( !defined $image ){
118        $image = Uploader::Image->new(
119            {
120                file              => $file->{ANSIPathName},
121                image_id          => $image_id,
122                caption           => $self->default_caption_cbk->(),
123                site_author       => $self->author,
124                add_rank          => $i,
125                site_tags         => [],
126                site_high_file    => $file->{ANSIPathName},
127                global_rank       => $self->global_rank,
128            }
129        );
130
131        $image->site_name(
132            $self->init_caption_from_pattern(
133                $image->file,
134                $image->create_date,
135                $image->add_rank,
136                $self->default_caption_cbk->(),
137                $self->default_caption_pattern_cbk->()
138            )
139        );
140
141        $self->create_wx_thumbnail_cbk->($image)
142            if 'CODE' eq ref $self->create_wx_thumbnail_cbk;
143
144        $self->_images->{$image_id} = $image ;
145    }
146
147    $self->add_images_cbk->($image->wx_thumb_file)
148       if 'CODE' eq ref $self->add_images_cbk;
149
150    $image;
151}
152
153
154sub new_image_id {
155    my ( $self, $filename ) = @_;
156
157    my $image_id = file_md5_hex(
158        $filename
159    );
160
161    $image_id;
162}
163
164
165sub Store {
166    my ( $self ) = @_;
167   
168    my $data = $self->get_storable(
169        [ 
170            qw/
171                version
172                storable_file
173            /,
174            @storable_properties
175        ] 
176   
177    );
178    eval {
179        #print Dumper $data;
180        store $data, $self->storable_file if defined $self->storable_file;
181    };
182    if($@){
183        print $@, "\n";   
184    }
185}
186
187
188sub set_current_image {
189    my ( $self, $indx ) = @_;
190    $self->current_image(
191        $indx != -1 ? $self->get_image($indx) : Uploader::Image->new()
192    );
193}
194
195
196sub get_image {
197    my ( $self, $indx ) = @_;
198   
199    return unless defined $indx;
200    my $id = $self->ordered_image_ids->[$indx];
201
202    $self->_images->{$id};
203}
204
205
206sub Image {
207    my ( $self, $id ) = @_;
208   
209    $self->_images->{$id};
210}
211
212
213sub is_empty {
214    my ( $self ) = @_;
215
216    !$self->image_count;
217}
218
219
220sub image_count {
221    my ( $self ) = @_;
222
223    scalar @{$self->ordered_image_ids};
224}
225
226
227sub selection_count {
228    my ( $self ) = @_;
229
230    scalar @{$self->selection};
231}
232
233
234sub remove_selection {
235    my ( $self ) = @_;
236   
237    return if ( $self->is_empty );
238    return if (! defined $self->selection );
239   
240    $self->_remove_selection($self->selection);
241    # clear image selection
242    $self->selection([]);
243}
244
245
246sub remove_processed {
247    my ( $self ) = @_;
248   
249    return if ( $self->is_empty );
250    return if (! defined $self->to_be_removed_grank );
251    my $to_remove_indx = [];
252    # check if the global rank correspond
253    # the image may have changed
254    foreach(@{$self->to_be_removed_grank}) {
255        my $indx = $self->global_rank_indx->{$_};
256        my $img;
257        if(defined $indx){
258            $img = $self->get_image($indx);
259        }
260        if(defined $img){
261            push @$to_remove_indx, $indx if
262                $img->global_rank == $_;
263        }
264    } 
265
266    $self->_remove_selection($to_remove_indx);
267    # clear image selection
268    $self->to_be_removed_grank([]);
269}
270
271
272sub _remove_selection {
273    my ( $self, $list ) = @_;
274
275    # the list is sorted, ascendant
276    # we reverse it to have
277    # higher first, and keep same indexes while deleting
278    @$list = reverse @$list;     
279    map {
280        $self->delete_image($_);
281        $self->delete_images_cbk->($_);
282        splice @{$self->ordered_image_ids}, $_, 1 ;
283        shift @$list;
284    }
285    @$list;
286
287}
288
289
290# postpone actual removal.
291sub set_to_be_removed {
292    my ( $self, $global_rank ) = @_;
293
294    push @{$self->to_be_removed_grank}, $global_rank;
295}
296
297
298sub delete_image {
299    my ( $self, $indx ) = @_;
300   
301    my $id = $self->ordered_image_ids->[$indx];
302
303    # have to check if that id is used more than once
304    my $count = grep { $id eq $_} @{$self->ordered_image_ids};
305
306    # global rank is unique
307    delete $self->global_rank_indx->{
308        $self->_images->{$id}->global_rank
309    };
310
311    delete $self->_images->{$id} unless $count > 1;
312}
313
314
315sub multi_selection_mode {
316    my ( $self ) = @_;
317
318    scalar @{$self->selection} > 1;
319}
320
321
322sub set_image_selection_tags {
323    my ( $self, $tags ) = @_;
324
325    $self->selection_tags($tags) if 'ARRAY' eq ref $tags;
326
327    # append to each image
328    # if multiple selection
329    if($self->multi_selection_mode){
330        map {
331            # need to dedup
332            my $tags = [
333                @{$self->get_image($_)->site_tags},
334                @{$self->selection_tags},
335            ];
336            #deduplicate
337            my $uniq = {
338                 map { $_ => 1 } @$tags
339            };
340            @$tags = keys %$uniq;
341            $self->get_image($_)->site_tags(
342                $tags
343            );
344        }@{$self->selection};
345    }
346
347    $self->selection_tags;
348}
349
350
351sub set_image_selection_privacy_level {
352    my ( $self, $privacy_level ) = @_;
353
354    # append to each image
355    # if multiple selection
356    if($self->multi_selection_mode){
357        if(defined $privacy_level){
358            $self->selection_privacy_level($privacy_level);
359            map {
360                $self->get_image($_)->privacy_level(
361                    $privacy_level
362                ) ;
363            }@{$self->selection}
364        };
365    }
366
367    $self->selection_privacy_level;
368}
369
370
371sub set_image_selection_name {
372    my ( $self, $name, $param ) = @_;
373   
374    # works in single and multi selection mode
375    if(defined $name){
376        map {
377            my $_name;
378            if( 'CODE' eq ref $name ){
379                $_name = $name->($_, $param);
380            }
381            else{
382                $_name = $name;
383                $self->get_image($_)->caption(
384                    $_name
385                ) ;
386            }
387
388            $self->selection_name($_name);
389            $self->get_image($_)->site_name(
390                $_name
391            ) ;
392        }@{$self->selection}
393    }
394}
395
396
397sub set_image_selection_author {
398    my ( $self, $author ) = @_;
399
400    # append to each image
401    # if multiple selection
402    if($self->multi_selection_mode){
403        if(defined $author){
404            $self->selection_author($author);
405            map {
406                $self->get_image($_)->site_author(
407                    $author
408                ) ;
409            }@{$self->selection}
410        };
411    }
412
413    $self->selection_author;
414}
415
416sub set_image_selection_comment {
417    my ( $self, $comment ) = @_;
418
419    # append to each image
420    # if multiple selection
421    if($self->multi_selection_mode){
422        if(defined $comment){
423            $self->selection_comment($comment);
424            map {
425                $self->get_image($_)->site_comment(
426                    $comment
427                ) ;
428            }@{$self->selection}
429        };
430    }
431
432    $self->selection_comment;
433}
434
435
436sub set_image_selection_create_date {
437    my ( $self, $date ) = @_;
438
439    # append to each image
440    # if multiple selection
441    if($self->multi_selection_mode){
442        if(defined $date){
443            $self->selection_create_date($date);
444            map {
445                $self->get_image($_)->create_date(
446                    $date
447                ) ;
448            }@{$self->selection}
449        };
450    }
451
452    $self->selection_create_date;
453}
454
455
456sub get_current_image_caption {
457    my ( $self, $index, $pattern ) = @_;
458
459    $pattern = $self->eng_caption_patterns->{$pattern};
460
461    $self->set_current_image($index);
462
463    my $img = $self->current_image;
464
465    $self->init_caption_from_pattern(
466        $img->file,
467        $img->create_date,
468        $index,
469        $self->current_image->caption,
470        $pattern
471    );
472}
473
474
475sub init_caption_from_pattern {
476    my ( $self, $file, $create_date, $i, $caption, $pattern ) = @_;
477
478    my ( $yyyy, $mm, $dd, $hh, $mi, $ss ) = split /[:\s]/, $create_date ;
479 
480    my $chrono = join('', $yyyy, $mm, $dd);
481
482    my $caption_from_pattern;
483    my $ext;
484    my ( $vol, $path, $filename ) = File::Spec->splitpath($file);
485    ( $filename, $ext ) = split /\.\w+$/, $filename;
486   
487
488    if('Caption' eq $pattern){
489        $caption_from_pattern = $caption
490    }
491    elsif('File name' eq $pattern){
492        $caption_from_pattern = $filename
493    }
494    elsif('File path and name' eq $pattern){
495        $caption_from_pattern = sprintf(
496            "%s", 
497            File::Spec->catfile($path, $filename), 
498        )       
499    }   
500    elsif('Caption + rank number' eq $pattern){
501        $caption_from_pattern = sprintf(
502            "%s %s", 
503            $caption, 
504            1+$i,
505        )       
506    }   
507    elsif('Rank number + caption' eq $pattern){
508        $caption_from_pattern = sprintf(
509            "%s %s", 
510            1+$i,
511            $caption, 
512        )       
513    }   
514    elsif('Caption + create date chrono' eq $pattern){
515        $caption_from_pattern = sprintf(
516            "%s %s", 
517            $caption, 
518            $chrono,
519        )       
520    }   
521    elsif('Create date chrono + caption' eq $pattern){
522        $caption_from_pattern = sprintf(
523            "%s %s", 
524            $chrono,
525            $caption, 
526        )       
527    }
528    elsif('Create date chrono + rank' eq $pattern){
529        $caption_from_pattern = sprintf(
530            "%s %s", 
531            $chrono,
532            1+$i, 
533        )       
534    }
535    elsif('Rank + create date chrono' eq $pattern){
536        $caption_from_pattern = sprintf(
537            "%s %s", 
538            1+$i, 
539            $chrono,
540        )       
541    }
542
543    $caption_from_pattern;
544}
545
546
547# we cannot send objects in other threads
548# and thread use data as they were when the thread is created
549sub get_images {
550    my ( $self, $preferences, $destination_category, $all ) = @_;
551
552
553    $self->get_images_data(
554        $self->selection_image_ids($all),
555        $preferences,
556        $destination_category
557    );
558}
559
560
561sub selection_image_ids {
562    my ( $self, $all ) = @_;
563
564    $all ?
565    $self->ordered_image_ids :
566    [
567        map {
568            $self->ordered_image_ids->[$_]
569        }@{$self->selection}
570    ];
571}
572
573
574# set piwigo id for already uploaded images.
575sub set_pwg_id {
576    my ( $self, $pwg_ids ) = @_;
577
578    map {
579        $self->_images->{$_}->{pwg_image_id} = $pwg_ids->{$_};
580    } @{$self->ordered_image_ids};
581}
582
583my $add_rank = 0;
584# copy data from image objects
585sub get_images_data {
586    my ( $self, $image_id_list, $preferences, $destination_category ) = @_;
587
588
589    return [
590        map{
591            $self->Image($_)->get_data($preferences, $destination_category, $add_rank++)
592        }@$image_id_list
593    ]
594}
595
596
597# when the progress list is cleared
598sub reset_add_rank {
599    my ( $self ) = @_;
600
601    $add_rank = 0;
602}
6031;
Note: See TracBrowser for help on using the repository browser.