| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Mojolicious::Command::Author::generate::obrazi; |
|
2
|
1
|
|
|
1
|
|
1066
|
use feature ':5.26'; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
133
|
|
|
3
|
1
|
|
|
1
|
|
8
|
use Mojo::Base Mojolicious::Command => -signatures; |
|
|
1
|
|
|
|
|
13
|
|
|
|
1
|
|
|
|
|
14
|
|
|
4
|
1
|
|
|
1
|
|
5085
|
use Mojo::File 'path'; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
62
|
|
|
5
|
1
|
|
|
1
|
|
11
|
use Mojo::Util qw(url_escape punycode_decode punycode_encode getopt encode decode dumper); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
85
|
|
|
6
|
1
|
|
|
1
|
|
7
|
use Mojo::Collection 'c'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
59
|
|
|
7
|
1
|
|
|
1
|
|
1229
|
use Text::CSV_XS qw( csv ); |
|
|
1
|
|
|
|
|
13722
|
|
|
|
1
|
|
|
|
|
78
|
|
|
8
|
1
|
|
|
1
|
|
8
|
use Mojo::Loader qw(data_section); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
53
|
|
|
9
|
1
|
|
|
1
|
|
1109
|
use Imager; |
|
|
1
|
|
|
|
|
40105
|
|
|
|
1
|
|
|
|
|
10
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $_formats = join '|', map { $_ =~ /jpe?g/i ? 'jpe?g' : $_ } sort keys %Imager::formats; |
|
12
|
|
|
|
|
|
|
my $FILETYPES = qr/\.(?:$_formats)$/i; |
|
13
|
0
|
|
|
0
|
|
|
my sub _U {'UTF-8'} |
|
14
|
|
|
|
|
|
|
has description => 'Generate a gallery from a directory structure with images'; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# our own log, used instead of 'say'. |
|
17
|
|
|
|
|
|
|
has log => sub { |
|
18
|
|
|
|
|
|
|
Mojo::Log->new(format => sub { "[$$] [$_[1]] " . join(' ', @_[2 .. $#_]) . $/ }); |
|
19
|
|
|
|
|
|
|
}; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
has usage => sub { shift->extract_usage . $/ . 'Supported formats: ' . $FILETYPES . $/ }; |
|
22
|
|
|
|
|
|
|
has from_dir => sub { path('./')->to_abs }; |
|
23
|
|
|
|
|
|
|
has images => sub { |
|
24
|
|
|
|
|
|
|
$_[0]->matrix->grep(sub { $_->[1] =~ $FILETYPES }); |
|
25
|
|
|
|
|
|
|
}; |
|
26
|
|
|
|
|
|
|
has _linked_images => sub { |
|
27
|
|
|
|
|
|
|
my $c = 0; |
|
28
|
|
|
|
|
|
|
my $images = $_[0]->images; |
|
29
|
|
|
|
|
|
|
return {map { $_->[1] => {self => $_, prev => $images->[$c - 1], next => $images->[$c + 1], id => ++$c} } @$images}; |
|
30
|
|
|
|
|
|
|
}; |
|
31
|
|
|
|
|
|
|
has categories => sub { |
|
32
|
|
|
|
|
|
|
$_[0]->matrix->grep(sub { !$_->[-1] && !$_->[-2] && $_->[1] =~ /$_->[0]$/ }); |
|
33
|
|
|
|
|
|
|
}; |
|
34
|
|
|
|
|
|
|
has files_per_subproc => sub { |
|
35
|
|
|
|
|
|
|
return int(scalar(@{$_[0]->images}) / $_[0]->subprocs_num) + 1; |
|
36
|
|
|
|
|
|
|
}; |
|
37
|
|
|
|
|
|
|
has csv_filename => 'index.csv'; |
|
38
|
|
|
|
|
|
|
has subprocs_num => 4; |
|
39
|
|
|
|
|
|
|
has template_file => ''; |
|
40
|
|
|
|
|
|
|
has obrazec_file => ''; |
|
41
|
|
|
|
|
|
|
has to_dir => sub { $_[0]->app->home->child('public') }; |
|
42
|
|
|
|
|
|
|
has publish_url => '/obrazi.html'; |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Default titles and descriptions |
|
45
|
|
|
|
|
|
|
has defaults => sub { { |
|
46
|
|
|
|
|
|
|
author => 'Марио Беров', |
|
47
|
|
|
|
|
|
|
category_title => 'Заглавие на категорията', |
|
48
|
|
|
|
|
|
|
category_description => 'Описание на категорията', |
|
49
|
|
|
|
|
|
|
image_title => 'Заглавие на изображението', |
|
50
|
|
|
|
|
|
|
image_description => 'Описание на изображението' |
|
51
|
|
|
|
|
|
|
. $/ |
|
52
|
|
|
|
|
|
|
. ' Материали, размери,какво, защо - според каквото мислиш, че е важно.', |
|
53
|
|
|
|
|
|
|
} }; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# An empty Imager instance on which the read() method will be called for every |
|
56
|
|
|
|
|
|
|
# image we work with. |
|
57
|
|
|
|
|
|
|
has imager => sub { Imager->new }; |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my @header = qw(category path title description author image thumbnail); |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# csv file contents |
|
62
|
|
|
|
|
|
|
has matrix => sub { c([@header]) }; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# resized images |
|
65
|
|
|
|
|
|
|
has _processed => sub { c() }; |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# '1000x1000' |
|
68
|
|
|
|
|
|
|
sub max { |
|
69
|
0
|
0
|
|
0
|
1
|
|
if ($_[1]) { |
|
70
|
0
|
0
|
0
|
|
|
|
$_[0]->{max} = $_[1] && return $_[0] if ref $_[1]; |
|
71
|
0
|
|
|
|
|
|
($_[0]->{max}{width}, $_[0]->{max}{height}) = $_[1] =~ /(\d+)x(\d+)/; |
|
72
|
0
|
|
|
|
|
|
return $_[0]; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
0
|
|
0
|
|
|
|
return $_[0]->{max} //= {width => 1000, height => 1000}; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# '100x100' |
|
78
|
|
|
|
|
|
|
sub thumbs { |
|
79
|
0
|
0
|
|
0
|
1
|
|
if ($_[1]) { |
|
80
|
0
|
0
|
0
|
|
|
|
$_[0]->{thumbs} = $_[1] && return $_[0] if ref $_[1]; |
|
81
|
0
|
|
|
|
|
|
($_[0]->{thumbs}{width}, $_[0]->{thumbs}{height}) = $_[1] =~ /(\d+)x(\d+)/; |
|
82
|
0
|
|
|
|
|
|
return $_[0]; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
0
|
|
0
|
|
|
|
return $_[0]->{thumbs} //= {width => 100, height => 100}; |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
|
|
87
|
0
|
|
|
0
|
1
|
|
sub run ($self, @args) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
88
|
0
|
|
|
|
|
|
getopt \@args, |
|
89
|
|
|
|
|
|
|
'f|from=s' => \(my $from_dir = $self->from_dir), |
|
90
|
|
|
|
|
|
|
'to=s' => \(my $to_dir = $self->to_dir), |
|
91
|
|
|
|
|
|
|
'x|max=s' => \(my $max = $self->max), |
|
92
|
|
|
|
|
|
|
's|thumbs=s' => \(my $thumbs = $self->thumbs), |
|
93
|
|
|
|
|
|
|
'i|index=s' => \(my $csv_filename = $self->csv_filename), |
|
94
|
|
|
|
|
|
|
't|template=s' => \(my $template = $self->template_file), |
|
95
|
|
|
|
|
|
|
'o|obrazec=s' => \(my $obrazec = $self->obrazec_file), |
|
96
|
|
|
|
|
|
|
'u|url=s' => \(my $url = $self->publish_url), |
|
97
|
|
|
|
|
|
|
; |
|
98
|
0
|
0
|
0
|
|
|
|
if ($template ne $self->template_file and not -f $template) { |
|
99
|
0
|
|
|
|
|
|
Carp::croak("Template $template does not exist. " . "Please provide an existing template file."); |
|
100
|
|
|
|
|
|
|
} |
|
101
|
0
|
0
|
|
|
|
|
$self->template_file($template) if $template; |
|
102
|
|
|
|
|
|
|
|
|
103
|
0
|
0
|
0
|
|
|
|
if ($obrazec ne $self->obrazec_file and not -f $obrazec) { |
|
104
|
0
|
|
|
|
|
|
Carp::croak("Single image template $obrazec does not exist. " . "Please provide an existing template file."); |
|
105
|
|
|
|
|
|
|
} |
|
106
|
0
|
0
|
|
|
|
|
$self->obrazec_file($obrazec) if $obrazec; |
|
107
|
|
|
|
|
|
|
|
|
108
|
0
|
|
|
|
|
|
$self->from_dir(path($from_dir)->to_abs)->to_dir(path($to_dir)->to_abs)->max($max)->thumbs($thumbs) |
|
109
|
|
|
|
|
|
|
->csv_filename($csv_filename)->publish_url(decode _U, $url); |
|
110
|
0
|
|
|
|
|
|
return $self->_do_csv->_resize_and_copy_to_dir->_do_html; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# Calculates the resized image dimensions according to the C<$self-Emax> |
|
114
|
|
|
|
|
|
|
# and C<$self-Ethumbs> gallery contraints. Accepts the utf8 decoded path |
|
115
|
|
|
|
|
|
|
# and the raw path to the file to be worked on. Returns two empty strings if |
|
116
|
|
|
|
|
|
|
# there is error reading the image and warns about the error. Returns filenames |
|
117
|
|
|
|
|
|
|
# for the resized image and the thumbnail image. |
|
118
|
0
|
|
|
0
|
1
|
|
sub calculate_max_and_thumbs ($self, $path, $raw_path) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
119
|
0
|
|
|
|
|
|
state $imager = $self->imager; |
|
120
|
0
|
|
|
|
|
|
my $log = $self->log; |
|
121
|
0
|
|
|
|
|
|
my $img; |
|
122
|
0
|
|
|
|
|
|
my $image = [$raw_path->to_array->[-1] =~ /^(.+?)\.(.\w+)$/]; |
|
123
|
0
|
|
|
|
|
|
$log->info('Inspecting image ', $path); |
|
124
|
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
|
my $max = $self->max; |
|
126
|
0
|
|
|
|
|
|
my $thumbs = $self->thumbs; |
|
127
|
0
|
|
|
|
|
|
my %size = %$max; |
|
128
|
0
|
|
|
|
|
|
my %thumb_size = %$thumbs; |
|
129
|
0
|
|
|
|
|
|
my $fh = $raw_path->open(); |
|
130
|
0
|
0
|
|
|
|
|
unless ($fh->binmode) { |
|
131
|
0
|
|
|
|
|
|
$log->warn(" !!! Skipping $path. Error: " . $!); |
|
132
|
0
|
|
|
|
|
|
return ('', ''); |
|
133
|
|
|
|
|
|
|
} |
|
134
|
0
|
0
|
|
|
|
|
if (not eval { $img = $imager->read(fh => $fh) }) { |
|
|
0
|
|
|
|
|
|
|
|
135
|
0
|
|
|
|
|
|
$log->warn(" !!! Skipping $path. Image error: " . $imager->errstr()); |
|
136
|
0
|
|
|
|
|
|
return ('', ''); |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
else { |
|
139
|
0
|
|
|
|
|
|
$image->[0] = decode _U, $image->[0]; |
|
140
|
0
|
|
|
|
|
|
%size = (width => $img->getwidth, height => $img->getheight); |
|
141
|
0
|
|
|
|
|
|
%thumb_size = %size; |
|
142
|
0
|
0
|
0
|
|
|
|
if ($size{width} > $max->{width} || $size{height} > $max->{height}) { |
|
143
|
|
|
|
|
|
|
@size{qw(x_scale y_scale width height)} |
|
144
|
0
|
|
|
|
|
|
= $img->scale_calculate(xpixels => $max->{width}, ypixels => $max->{height}, type => 'min'); |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
0
|
0
|
0
|
|
|
|
if ($thumb_size{width} > $thumbs->{width} || $thumb_size{height} > $thumbs->{height}) { |
|
148
|
|
|
|
|
|
|
@thumb_size{qw(x_scale y_scale width height)} |
|
149
|
0
|
|
|
|
|
|
= $img->scale_calculate(xpixels => $thumbs->{width}, ypixels => $thumbs->{height}, type => 'min'); |
|
150
|
|
|
|
|
|
|
} |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
return ( |
|
154
|
0
|
|
|
|
|
|
punycode_encode($image->[0]) . "_$size{width}x$size{height}.$image->[1]", |
|
155
|
|
|
|
|
|
|
punycode_encode($image->[0]) . "_$thumb_size{width}x$thumb_size{height}.$image->[1]" |
|
156
|
|
|
|
|
|
|
); |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Reads the `from_dir` and dumps a csv file named after the from_dir folder. |
|
160
|
|
|
|
|
|
|
# The file contains a table with paths and default titles and descriptions for |
|
161
|
|
|
|
|
|
|
# the pictures. This file can be given to the painter to add titles and |
|
162
|
|
|
|
|
|
|
# descriptions for the pictures using an application like LibreOffice Calc or |
|
163
|
|
|
|
|
|
|
# M$ Excel. |
|
164
|
0
|
|
|
0
|
|
|
sub _do_csv ($self, $root = $self->from_dir) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
|
my $csv_filepath = decode _U, $root->child($self->csv_filename); |
|
166
|
0
|
|
|
|
|
|
my $log = $self->log; |
|
167
|
0
|
0
|
|
|
|
|
if (-f $csv_filepath) { |
|
168
|
0
|
|
|
|
|
|
$log->info("$csv_filepath already exists.$/" |
|
169
|
|
|
|
|
|
|
. "\tContinuing with resizing and copying images.$/" |
|
170
|
|
|
|
|
|
|
. "\tSome rows may be updated with filenames for resized images and thumbnails...$/"); |
|
171
|
0
|
|
|
|
|
|
return $self; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
0
|
|
|
|
|
|
my $category = ''; |
|
174
|
0
|
|
|
|
|
|
my $defaults = $self->defaults; |
|
175
|
0
|
|
|
|
|
|
my $matrix = $self->matrix; |
|
176
|
0
|
|
|
|
|
|
$root->list_tree({dir => 1})->sort->each( |
|
177
|
0
|
|
|
0
|
|
|
sub ($e, $num) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
|
$self->_make_matrix_row($root, $e, \$category, $defaults, $matrix, $log); |
|
179
|
|
|
|
|
|
|
} |
|
180
|
0
|
|
|
|
|
|
); |
|
181
|
0
|
|
|
|
|
|
csv(in => $matrix->to_array, enc => _U, out => \my $data, binary => 1, sep_char => ","); |
|
182
|
0
|
|
|
|
|
|
path($csv_filepath)->spurt($data); |
|
183
|
|
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
return $self; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
0
|
|
|
0
|
|
|
sub _make_matrix_row ($self, $root, $raw_path, $category, $defaults, $matrix, $log) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
my $path = decode(_U, $_->to_string =~ s|$root/||r); |
|
189
|
0
|
0
|
|
|
|
|
if (-d $raw_path) { |
|
|
|
0
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
$log->info("Inspecting category $path"); |
|
191
|
0
|
|
|
|
|
|
$$category = decode(_U, $_->to_array->[-1]); |
|
192
|
|
|
|
|
|
|
push @$matrix, |
|
193
|
|
|
|
|
|
|
[ |
|
194
|
|
|
|
|
|
|
$$category, $path, |
|
195
|
|
|
|
|
|
|
"$defaults->{category_title} – $$category", |
|
196
|
|
|
|
|
|
|
$defaults->{category_description}, |
|
197
|
0
|
|
|
|
|
|
$defaults->{author}, '', '', |
|
198
|
|
|
|
|
|
|
]; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
elsif (-f $raw_path) { |
|
201
|
0
|
0
|
|
|
|
|
if ($_ !~ $FILETYPES) { |
|
202
|
0
|
|
|
|
|
|
$log->warn("Skipping unsupported file $path…"); |
|
203
|
0
|
|
|
|
|
|
return; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# for images without category - which are in the $root folder |
|
207
|
0
|
0
|
|
|
|
|
$$category = '' unless $path =~ /$$category/; |
|
208
|
|
|
|
|
|
|
push @$matrix, |
|
209
|
|
|
|
|
|
|
[ |
|
210
|
|
|
|
|
|
|
$$category, $path, |
|
211
|
|
|
|
|
|
|
"$defaults->{image_title} – " . path($path)->basename, |
|
212
|
|
|
|
|
|
|
$defaults->{image_description}, |
|
213
|
0
|
|
|
|
|
|
$defaults->{author}, $self->calculate_max_and_thumbs($path, $raw_path) |
|
214
|
|
|
|
|
|
|
]; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Scales and resizes images to maximum width and height and generates thumbnails. |
|
219
|
0
|
|
|
0
|
|
|
sub _resize_and_copy_to_dir($self) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
|
my $matrix = $self->matrix; |
|
221
|
0
|
|
|
|
|
|
my $csv_filepath = decode _U, $self->from_dir->child($self->csv_filename); |
|
222
|
0
|
0
|
|
|
|
|
if (@$matrix == 1) { |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# read the CSV file from disk to get calculated dimensions |
|
225
|
0
|
|
|
|
|
|
$matrix = c @{csv(in => $csv_filepath, enc => _U, binary => 1, sep_char => ",")}; |
|
|
0
|
|
|
|
|
|
|
|
226
|
0
|
|
|
|
|
|
$self->matrix($matrix); |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
|
my @subprocs; |
|
230
|
0
|
|
|
|
|
|
my $chunk_size = $self->files_per_subproc; |
|
231
|
0
|
|
|
0
|
|
|
my $images = $self->images->map(sub { [@$_] }); #copy |
|
|
0
|
|
|
|
|
|
|
|
232
|
0
|
|
|
|
|
|
while (my @chunk = splice @$images, 0, $chunk_size) { |
|
233
|
0
|
|
|
|
|
|
push @subprocs, $self->_process_chunk_of_files(\@chunk); |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
0
|
|
|
|
|
|
foreach my $s (@subprocs) { |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# Wait for the subprocess to finish |
|
239
|
0
|
|
|
|
|
|
$s->wait; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
0
|
|
|
|
|
|
$self->log->info('All subprocesses finished.'); |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# Update image and thumbnail columns and write the new csv file |
|
244
|
0
|
|
|
|
|
|
my $_processed = $self->_processed; |
|
245
|
0
|
|
|
|
|
|
my $updated = 0; |
|
246
|
|
|
|
|
|
|
$self->matrix->each(sub { |
|
247
|
0
|
0
|
|
0
|
|
|
return unless $_->[1] =~ $FILETYPES; |
|
248
|
0
|
|
|
|
|
|
for my $i (0 .. @$_processed - 1) { |
|
249
|
0
|
0
|
0
|
|
|
|
if ($_->[1] eq $_processed->[$i][1] && ($_->[-1] ne $_processed->[$i][-1] || $_->[-2] ne $_processed->[$i][-2])) { |
|
|
|
|
0
|
|
|
|
|
|
250
|
0
|
|
|
|
|
|
($_) = splice @$_processed, $i, 1; |
|
251
|
0
|
|
|
|
|
|
$updated++; |
|
252
|
0
|
|
|
|
|
|
last; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
} |
|
255
|
0
|
|
|
|
|
|
}); |
|
256
|
0
|
0
|
|
|
|
|
if ($updated) { |
|
257
|
0
|
|
|
|
|
|
csv(in => $matrix->to_array, enc => _U, out => \my $data, binary => 1, sep_char => ","); |
|
258
|
0
|
|
|
|
|
|
path($csv_filepath)->spurt($data); |
|
259
|
0
|
|
|
|
|
|
my $copied = path($csv_filepath)->copy_to($self->to_dir); |
|
260
|
0
|
|
|
|
|
|
$self->log->info("$csv_filepath *updated* and copied to $copied"); |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
else { |
|
263
|
0
|
|
|
|
|
|
my $copied = path($csv_filepath)->copy_to($self->to_dir); |
|
264
|
0
|
|
|
|
|
|
$self->log->info("$csv_filepath copied to $copied"); |
|
265
|
|
|
|
|
|
|
} |
|
266
|
0
|
|
|
|
|
|
return $self; |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
0
|
|
|
0
|
|
|
sub _process_chunk_of_files ($self, $files = []) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
270
|
0
|
|
|
|
|
|
my $log = $self->log; |
|
271
|
0
|
|
|
|
|
|
my $from_dir = $self->from_dir; |
|
272
|
0
|
|
|
|
|
|
my $to_dir = $self->to_dir; |
|
273
|
0
|
|
|
|
|
|
my $imager = $self->imager; |
|
274
|
0
|
|
|
0
|
|
|
Mojo::IOLoop->subprocess->run_p(sub($sub) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
275
|
0
|
|
|
|
|
|
my $processed = []; |
|
276
|
0
|
|
|
|
|
|
for my $row (@$files) { |
|
277
|
0
|
|
|
|
|
|
my $raw_path = $from_dir->child(encode _U, $row->[1]); |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# Check for calculated dimensions and calculate them if missing. |
|
280
|
0
|
0
|
0
|
|
|
|
if (!$row->[-2] || !$row->[-1]) { |
|
281
|
0
|
|
|
|
|
|
($row->[-2], $row->[-1]) = $self->calculate_max_and_thumbs($row->[1], $raw_path); |
|
282
|
|
|
|
|
|
|
} |
|
283
|
0
|
|
|
|
|
|
my $to_path = $to_dir->child($row->[1])->dirname; |
|
284
|
0
|
|
|
|
|
|
my $sized_path = $to_path->child($row->[-2])->to_string; |
|
285
|
0
|
|
|
|
|
|
my $thumb_path = $to_path->child($row->[-1])->to_string; |
|
286
|
0
|
|
|
|
|
|
my $html_path = "$sized_path.html"; |
|
287
|
0
|
0
|
0
|
|
|
|
if (-s $sized_path && -s $thumb_path && -s $html_path) { |
|
|
|
|
0
|
|
|
|
|
|
288
|
0
|
|
|
|
|
|
$log->info("$row->[-2].html, $row->[-2] and $row->[-1] were already produced. Skipping ..."); |
|
289
|
0
|
|
|
|
|
|
push @$processed, $row; |
|
290
|
0
|
|
|
|
|
|
next; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
0
|
|
|
|
|
|
my (%sized, %thumb); |
|
293
|
0
|
|
|
|
|
|
@sized{qw(xpixels ypixels)} = $row->[-2] =~ /_(\d+)x(\d+)\./; |
|
294
|
0
|
|
|
|
|
|
@thumb{qw(xpixels ypixels)} = $row->[-1] =~ /_(\d+)x(\d+)\./; |
|
295
|
0
|
|
|
|
|
|
my $img; |
|
296
|
0
|
0
|
|
|
|
|
unless (-s $sized_path) { |
|
297
|
0
|
0
|
|
|
|
|
unless (eval { $img = $imager->read(file => $raw_path) }) { |
|
|
0
|
|
|
|
|
|
|
|
298
|
0
|
|
|
|
|
|
$log->warn(" !!! Skipping $row->[1]. Image error: " . $imager->errstr()); |
|
299
|
0
|
|
|
|
|
|
next; |
|
300
|
|
|
|
|
|
|
} |
|
301
|
0
|
0
|
|
|
|
|
unless (eval { $to_path->make_path({mode => 0711}); 1 }) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
302
|
0
|
|
|
|
|
|
$log->warn("!!! Skipping $row->[1]. Error: $@"); |
|
303
|
0
|
|
|
|
|
|
next; |
|
304
|
|
|
|
|
|
|
} |
|
305
|
0
|
|
|
|
|
|
my $maxi = $img->scale(%sized); |
|
306
|
0
|
|
|
|
|
|
$maxi->settag(name => 'i_xres', value => 96); |
|
307
|
0
|
|
|
|
|
|
$maxi->settag(name => 'i_yres', value => 96); |
|
308
|
0
|
0
|
|
|
|
|
unless ($maxi->write(file => $sized_path)) { |
|
309
|
0
|
|
|
|
|
|
$log->warn("!!! Cannot write image $sized_path!\nError:" . $maxi->errstr); |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
else { |
|
312
|
0
|
|
|
|
|
|
$log->info("Written $sized_path"); |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
} |
|
315
|
0
|
0
|
|
|
|
|
unless (-s $thumb_path) { |
|
316
|
0
|
|
|
|
|
|
my $thumbi = $img->scale(%thumb); |
|
317
|
0
|
|
|
|
|
|
$thumbi->settag(name => 'i_xres', value => 96); |
|
318
|
0
|
|
|
|
|
|
$thumbi->settag(name => 'i_yres', value => 96); |
|
319
|
0
|
0
|
|
|
|
|
unless ($thumbi->write(file => $thumb_path)) { |
|
320
|
0
|
|
|
|
|
|
$log->warn("!!! Cannot write image $thumb_path!\nError:" . $thumbi->errstr); |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
else { |
|
323
|
0
|
|
|
|
|
|
$log->info("Written $thumb_path"); |
|
324
|
|
|
|
|
|
|
} |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# Generate single image html file for sharing on social medias |
|
328
|
0
|
|
|
|
|
|
$self->render_obrazec_to_file($row, $html_path); |
|
329
|
0
|
|
|
|
|
|
push @$processed, $row; |
|
330
|
|
|
|
|
|
|
} |
|
331
|
0
|
|
|
|
|
|
return $$, $processed; |
|
332
|
0
|
|
|
|
|
|
})->then( |
|
333
|
0
|
|
|
0
|
|
|
sub ($pid, $processed) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Executed in the parent process where we can collect the results and write the |
|
336
|
|
|
|
|
|
|
# new csv file, which can be saved in the $to_dir. |
|
337
|
|
|
|
|
|
|
# TODO: think if this is needed or we can just copy the initially produced |
|
338
|
|
|
|
|
|
|
# csv file to $to_dir. |
|
339
|
0
|
|
|
|
|
|
$log->info("PID $pid processed " . (scalar @$processed) . ' images!'); |
|
340
|
0
|
|
|
|
|
|
push @{$self->_processed}, @$processed; |
|
|
0
|
|
|
|
|
|
|
|
341
|
0
|
|
|
|
|
|
return; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
0
|
|
|
0
|
|
|
)->catch(sub ($err) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
344
|
0
|
0
|
|
|
|
|
$log->warn("Subprocess error: $err") if $err; |
|
345
|
0
|
|
|
|
|
|
}); |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
|
|
348
|
0
|
|
|
0
|
|
|
sub _do_html($self) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
349
|
0
|
|
|
|
|
|
state $app = $self->app; |
|
350
|
0
|
|
|
|
|
|
my $categories = $self->categories; |
|
351
|
0
|
|
|
|
|
|
my $processed = $self->_processed; |
|
352
|
0
|
|
|
|
|
|
my $template_file = $self->template_file; |
|
353
|
0
|
|
0
|
|
|
|
my $tpl = $template_file || 'obrazi.html'; |
|
354
|
0
|
|
|
|
|
|
my $css_file = 'obrazi.css'; |
|
355
|
0
|
|
|
|
|
|
my $js_file = 'obrazi.js'; |
|
356
|
0
|
|
|
|
|
|
my $vars = { |
|
357
|
|
|
|
|
|
|
generator => __PACKAGE__, |
|
358
|
|
|
|
|
|
|
categories => $categories, |
|
359
|
|
|
|
|
|
|
processed => $processed, |
|
360
|
|
|
|
|
|
|
app => $app, |
|
361
|
|
|
|
|
|
|
thumbs => $self->thumbs, |
|
362
|
|
|
|
|
|
|
css_file => $css_file, |
|
363
|
|
|
|
|
|
|
js_file => $js_file, |
|
364
|
|
|
|
|
|
|
linked => $self->_linked_images, |
|
365
|
|
|
|
|
|
|
self => $self, |
|
366
|
|
|
|
|
|
|
}; |
|
367
|
|
|
|
|
|
|
|
|
368
|
0
|
|
|
|
|
|
$self->write_file($self->to_dir->child($css_file), $self->render_data($css_file => $vars)); |
|
369
|
0
|
|
|
|
|
|
$self->write_file($self->to_dir->child($js_file), $self->render_data($js_file => $vars)); |
|
370
|
0
|
0
|
|
|
|
|
if ($template_file) { |
|
371
|
|
|
|
|
|
|
|
|
372
|
0
|
|
|
|
|
|
my $html = Mojo::Template->new($self->template)->name($template_file)->render_file($template_file => $vars); |
|
373
|
0
|
|
|
|
|
|
$self->to_dir->child(path($template_file)->basename =~ s/\.ep$//r)->spurt(encode _U, $html); |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
else { |
|
376
|
0
|
|
|
|
|
|
$self->write_file($self->to_dir->child($tpl), encode _U, $self->render_data($tpl => $vars)); |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
|
return $self; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
|
|
382
|
0
|
|
|
0
|
0
|
|
sub render_obrazec_to_file ($self, $img, $html_path) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
|
state $obrazec_file = $self->obrazec_file; |
|
384
|
0
|
|
|
|
|
|
state $obrazec_data = 'obrazec.html'; |
|
385
|
0
|
|
0
|
|
|
|
state $mt = Mojo::Template->new(vars => 1, name => $obrazec_file || $obrazec_data); |
|
386
|
0
|
|
|
|
|
|
state $parsed = 0; |
|
387
|
0
|
|
|
|
|
|
my $categories = $self->categories; |
|
388
|
|
|
|
|
|
|
|
|
389
|
0
|
0
|
0
|
|
|
|
if ($obrazec_file && !$parsed) { |
|
|
|
0
|
0
|
|
|
|
|
|
390
|
0
|
|
|
|
|
|
$mt->parse(decode _U, path($obrazec_file)->slurp); |
|
391
|
0
|
|
|
|
|
|
$parsed = 1; |
|
392
|
|
|
|
|
|
|
} |
|
393
|
|
|
|
|
|
|
elsif (!$obrazec_file && !$parsed) { |
|
394
|
0
|
|
|
|
|
|
$mt->parse(data_section(ref $self, $obrazec_data)); |
|
395
|
0
|
|
|
|
|
|
$parsed = 1; |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
|
|
398
|
0
|
|
|
|
|
|
my $html = $mt->process( |
|
399
|
|
|
|
|
|
|
{ |
|
400
|
|
|
|
|
|
|
generator => __PACKAGE__, |
|
401
|
|
|
|
|
|
|
self => $self, |
|
402
|
|
|
|
|
|
|
app => $self->app, |
|
403
|
|
|
|
|
|
|
img => $img, |
|
404
|
|
|
|
|
|
|
categories => $categories, |
|
405
|
|
|
|
|
|
|
linked => $self->_linked_images |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
); |
|
408
|
0
|
0
|
|
|
|
|
path($html_path)->spurt(encode _U, $html) && $self->log->info("Written $html_path"); |
|
409
|
0
|
|
|
|
|
|
return; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
1; |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=encoding utf8 |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=head1 NAME |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Mojolicious::Command::Author::generate::obrazi - Обраꙁи for your site – a |
|
418
|
|
|
|
|
|
|
gallery generator command |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Usage: APPLICATION generate obrazi [OPTIONS] |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
Examples: |
|
425
|
|
|
|
|
|
|
./myapp.pl help generate obrazi # This help |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
mojo generate obrazi --from ~/Pictures/summer-2021 \ |
|
428
|
|
|
|
|
|
|
--to /opt/myapp/public/summer-2021 |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
mojo generate obrazi --from ~/Pictures/summer-2021 \ |
|
431
|
|
|
|
|
|
|
--to /opt/some/static/site/albums/summer-2021 \ |
|
432
|
|
|
|
|
|
|
-x 800x600 -s 96x96 -t ./some/custom_template.html.ep |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Options: |
|
435
|
|
|
|
|
|
|
-h, --help Show this summary of available options |
|
436
|
|
|
|
|
|
|
-f, --from Root of directory structure from which the images |
|
437
|
|
|
|
|
|
|
will be taken. Defaults to ./. |
|
438
|
|
|
|
|
|
|
--to Root directory where the gallery will be put. Defaults to ./. |
|
439
|
|
|
|
|
|
|
-x, --max Maximum image dimesnions in pixels in format 'widthxheight'. |
|
440
|
|
|
|
|
|
|
Defaults to 1000x1000. |
|
441
|
|
|
|
|
|
|
-s, --thumbs Thumbnails maximal dimensions. Defaults to 100x100 pixels. |
|
442
|
|
|
|
|
|
|
-i, --index Name of the CSV index file to be generated and then read |
|
443
|
|
|
|
|
|
|
in the --from directory. |
|
444
|
|
|
|
|
|
|
-t, --template Path to template file. Defaults to embedded template |
|
445
|
|
|
|
|
|
|
obrazi.html. |
|
446
|
|
|
|
|
|
|
-o, --obrazec Path to single image template file for sharing on social |
|
447
|
|
|
|
|
|
|
media. Defaults to embedded template obrazec.html |
|
448
|
|
|
|
|
|
|
-u, --url Url where the gallery will be published. |
|
449
|
|
|
|
|
|
|
Defaults to '/obrazi.html'. |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
L generates a gallery from a |
|
454
|
|
|
|
|
|
|
directory structure, containing images. The produced gallery is a static html |
|
455
|
|
|
|
|
|
|
file which body content can be easily taken, modified, and embedded into a page |
|
456
|
|
|
|
|
|
|
in any site. |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
In addition the command generates a csv file on the first traversal of the |
|
459
|
|
|
|
|
|
|
directory structure, describing the images. This file can be edited. Titles and |
|
460
|
|
|
|
|
|
|
descriptions can be added for each image and then the command can be run again |
|
461
|
|
|
|
|
|
|
to regenerate the gallery with the modified titles and descriptions. This file |
|
462
|
|
|
|
|
|
|
can be further used by a helper — L to |
|
463
|
|
|
|
|
|
|
produce and embed the HTML for the galery into a L application. |
|
464
|
|
|
|
|
|
|
Please note that the helper is not yet implemented. |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
The word B<обраꙁъ>(singular) means L
|
|
467
|
|
|
|
|
|
|
template, etc.|https://histdict.uni-sofia.bg/dictionary/show/d_05616> |
|
468
|
|
|
|
|
|
|
in OCS/OS/OBG language. The name of the plugin is the plural variant in |
|
469
|
|
|
|
|
|
|
nominative case (обраꙁи). |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=head1 WORKFLOW |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
1. Images' owner and producer gives the direcory (probably zipped) to the |
|
474
|
|
|
|
|
|
|
command runner (a human yet). |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
2. The runner runs the command as shown in the SYNOPSIS. |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
3. The runner gives back the produced csv file to the images' producer. Fixes |
|
479
|
|
|
|
|
|
|
problems with ICC profiles etc. Notifies the producer for eventual naming |
|
480
|
|
|
|
|
|
|
convetions, possible problems. The producer fills in the description and titles |
|
481
|
|
|
|
|
|
|
in the comfort of L
|
|
482
|
|
|
|
|
|
|
Calc|https://www.libreoffice.org/discover/calc/> or MS Excel and returns the |
|
483
|
|
|
|
|
|
|
file to the command-runner. This may take some time. |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
4. The runner runs again the command with the modified csv file, reviews the |
|
486
|
|
|
|
|
|
|
produced file. Takes the HTML and puts it in a page on the Web. |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
5. The images' owner/producer enjoys the gallery, prise him/herself with it or |
|
489
|
|
|
|
|
|
|
goes back to the runner to report problems. |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
6. DONE or go to some of the previous steps. |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=head1 FEATURES |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
Recursively traverses subdirectories and scales images (four at a time) to |
|
496
|
|
|
|
|
|
|
given width and height and produces thumbnails for those images. Thumbnail |
|
497
|
|
|
|
|
|
|
sizes can also be set. |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
Produces an index CSV file which can be edited to add titles and descriptions |
|
500
|
|
|
|
|
|
|
for the images. |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Produces an HTML file with fully functional lightbox-like gallery, implemented |
|
503
|
|
|
|
|
|
|
only using jQuery and CSS – no jQ-plugins. Left/right keyboard buttons navigation |
|
504
|
|
|
|
|
|
|
to next and previous image. |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
L inherits all attributes from |
|
509
|
|
|
|
|
|
|
L and implements the following new ones. |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=head2 categories |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
my $cat = $self->categories->first(sub { $_->[0] eq $img->[0] }); |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
A L instance, containing rows from the CSV file which are |
|
516
|
|
|
|
|
|
|
categories (directories). For this attribute to return meaningful data, |
|
517
|
|
|
|
|
|
|
C<$self-Ematrix> must be already filled in from CSV file. |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head2 csv_filename |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
my $filename = $self->csv_filename; # index.csv |
|
523
|
|
|
|
|
|
|
my $обраꙁи = $self->csv_filename('gallery.csv'); |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
The name of the CSV file which will be created in L. This file, |
|
526
|
|
|
|
|
|
|
after being edited and after the images are processed, will be copied together |
|
527
|
|
|
|
|
|
|
with the images to L. Defaults to C. Can be passed on the |
|
528
|
|
|
|
|
|
|
command-line via the C<--index> argument. |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=head2 defaults |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
my $defaults_hashref = $обраꙁи->defaults; |
|
533
|
|
|
|
|
|
|
$обраꙁи->defaults->{category_title} = 'Def. Cat. title'; |
|
534
|
|
|
|
|
|
|
$обраꙁи->defaults->{category_description} = 'Def. Cat. description.'; |
|
535
|
|
|
|
|
|
|
$обраꙁи->defaults->{image_title} = 'Def. Image Title'; |
|
536
|
|
|
|
|
|
|
$обраꙁи->defaults->{image_description} = 'Def. Image description.'; |
|
537
|
|
|
|
|
|
|
$обраꙁи->defaults->{author} = 'John Smith'; |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
These values go to the folowing columns in the produced CSV file. C
|
|
540
|
|
|
|
|
|
|
description, author>. They are supposed to be replaced by editing the produced |
|
541
|
|
|
|
|
|
|
file. TODO: Maybe allow these to be passed on the command line via an argument |
|
542
|
|
|
|
|
|
|
C<--defaults>. |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=head2 description |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
my $description = $обраꙁи->description; |
|
547
|
|
|
|
|
|
|
$self = $обраꙁи->description('Foo'); |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
Short description of this command, used for the application's command list. |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=head2 files_per_subproc |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
my $files_num = $обраꙁи->files_per_subproc; |
|
554
|
|
|
|
|
|
|
$self = $обраꙁи->files_per_subproc(10); |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
Number of files to be processed by one subprocess. Defaults to |
|
557
|
|
|
|
|
|
|
Csubprocs_num) +1>. The last chunk of files is |
|
558
|
|
|
|
|
|
|
the remainder — usually smaller than the previous chunks. |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=head2 from_dir |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
$self = $обраꙁи->from_dir(path('./')); |
|
563
|
|
|
|
|
|
|
my $root_folder_abs_path = $обраꙁи->from_dir; |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
Holds a L instance — absolute path to the directory from which the |
|
566
|
|
|
|
|
|
|
pictures will be taken. This is where the CSV file describing the directory |
|
567
|
|
|
|
|
|
|
structure will be generated too. The value is taken from the commandline |
|
568
|
|
|
|
|
|
|
argument C<--from_dir>. Defaults to C<./> — current directory — where the |
|
569
|
|
|
|
|
|
|
command is executed. |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=head2 imager |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
my $img = $обраꙁи->imager->read(file=>'path/to/image.jpg') |
|
574
|
|
|
|
|
|
|
|| die $обраꙁи->imager->errstr; |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
my $self = $обраꙁи->imager(Imager->new); |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
An L instance. This is the images-processing engine, used by the |
|
579
|
|
|
|
|
|
|
command. |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=head2 images |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
my $images = $self->images->map(sub { [@$_] }); #copy |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
A L instance, containing rows from the CSV file which are |
|
586
|
|
|
|
|
|
|
image files, supported by L and will be processed. For this attribute |
|
587
|
|
|
|
|
|
|
to return meaningful data, C<$self-Ematrix> must be already filled in from |
|
588
|
|
|
|
|
|
|
CSV file. |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=head2 log |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
my $log = $self->log; |
|
593
|
|
|
|
|
|
|
my $self = $self->log(Mojo::Log->new) |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
A L instance. By default it is not the same as |
|
596
|
|
|
|
|
|
|
C<$self-Eapp-Elog>. Used to output info, warnings and errors to the |
|
597
|
|
|
|
|
|
|
terminal or the application log. |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=head2 matrix |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
my $matrix = $self->matrix; |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# add an image |
|
604
|
|
|
|
|
|
|
push @$matrix, |
|
605
|
|
|
|
|
|
|
[ |
|
606
|
|
|
|
|
|
|
$category, $path, |
|
607
|
|
|
|
|
|
|
$defaults->{image_title}, $defaults->{image_description}, |
|
608
|
|
|
|
|
|
|
$defaults->{author}, $self->calculate_max_and_thumbs($path, $raw_path) |
|
609
|
|
|
|
|
|
|
]; |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# add a category |
|
612
|
|
|
|
|
|
|
push @$matrix, [ |
|
613
|
|
|
|
|
|
|
$category, $path, "$defaults->{category_title} – $category", |
|
614
|
|
|
|
|
|
|
$defaults->{category_description}, $defaults->{author}, '', '' |
|
615
|
|
|
|
|
|
|
]; |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
$matrix->each(sub{...}); |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
csv(in => $matrix->to_array, enc => 'UTF-8', out => \my $data, binary => 1, sep_char => ","); |
|
621
|
|
|
|
|
|
|
path($csv_filepath)->spurt($data); |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
A L instance. First row contains the headers. This matrix is |
|
624
|
|
|
|
|
|
|
filled in while recursively searching in the L for images. Then it |
|
625
|
|
|
|
|
|
|
is dumped into the index CSV file. If the CSV file is already present, the data |
|
626
|
|
|
|
|
|
|
is read directly from it. |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=head2 max |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
my $max_sizes = $self->max; # {width => 1000, height => 1000} |
|
631
|
|
|
|
|
|
|
$self = $self->max({width => 1000, height => 1000}); |
|
632
|
|
|
|
|
|
|
$self = $self->max('1000x1000'); |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
A hash reference with keys C and C. Defaults to C<{width => |
|
635
|
|
|
|
|
|
|
1000, height => 1000}>. Can be changed via the command line argument C<--max>. |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=head2 obrazec_file |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
Path to template file for single html pages. Defaults to embedded template |
|
640
|
|
|
|
|
|
|
obrazec.html. Can be passed as argument on the command-line via C<--obrazec>. |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
=head2 publish_url |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
$обраꙁи->publish_url($string); # $self |
|
645
|
|
|
|
|
|
|
$обраꙁи->publish_url; # $string |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
String. Url path or preferably full url, where the gallery will reside. Needed |
|
648
|
|
|
|
|
|
|
for link from individual images to the common gallery page and OpenGraph meta |
|
649
|
|
|
|
|
|
|
data. Can be passed as argument on the command-line via C<--url>. Defaults |
|
650
|
|
|
|
|
|
|
to C – rarely what you need. |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=head2 subprocs_num |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
$self->subprocs_num; #4 |
|
655
|
|
|
|
|
|
|
$self = $self->subprocs_num(5); |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
Integer, used to split the number of files found into equal chunks, each of |
|
658
|
|
|
|
|
|
|
which will be processed in a separate subprocess in parallel. Defaults to 4. |
|
659
|
|
|
|
|
|
|
See also L. |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=head2 template_file |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
my $self = $self->template_file('path/to/template.html.ep'); |
|
664
|
|
|
|
|
|
|
my $tpl = $self->template_file; |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
Path to template file to be used for generating the HTML for the gallery. |
|
667
|
|
|
|
|
|
|
Defaults to embedded template obrazi.html. Can be passed as argument on the |
|
668
|
|
|
|
|
|
|
command-line via C<--template>. |
|
669
|
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=head2 thumbs |
|
671
|
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
my $thumbs_sizes = $self->thumbs; # {width => 100, height => 100} |
|
673
|
|
|
|
|
|
|
$self = $self->thumbs({width => 100, height => 100}); |
|
674
|
|
|
|
|
|
|
$self = $self->thumbs('1000x1000'); |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
A hash reference with keys C and C. Defaults to C<{width => |
|
677
|
|
|
|
|
|
|
100, height => 100}>. Can be changed via the command line argument |
|
678
|
|
|
|
|
|
|
C<--thumbs>. |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=head2 to_dir |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
$self->to_dir # path($app/public) |
|
683
|
|
|
|
|
|
|
$self = $self->to_dir(path('/some/folder')); |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
A L instance. Directory, where the folder with the processed images |
|
686
|
|
|
|
|
|
|
will be put. Defaults to the C forlder of the current application. |
|
687
|
|
|
|
|
|
|
Can be changed via the command line argument C<--to_dir>. It is recommended to |
|
688
|
|
|
|
|
|
|
pass this value unless all your images are into one root folder. The L |
|
689
|
|
|
|
|
|
|
directory is the root of your images' galery. |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=head2 usage |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
my $usage = $обраꙁи->usage; |
|
694
|
|
|
|
|
|
|
$self = $обраꙁи->usage('Foo'); |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
Usage information for this command, used for the help screen. At the bottom are |
|
697
|
|
|
|
|
|
|
shown the supported by L image formats. |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=head1 METHODS |
|
700
|
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
L inherits all methods from |
|
702
|
|
|
|
|
|
|
L and implements the following new ones. |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=head2 calculate_max_and_thumbs |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
# img_1000x1000.jpg, img_100x100.jpg |
|
707
|
|
|
|
|
|
|
my ($img_filename, $thumb_filename) = $self->calculate_max_and_thumbs($decoded_path, $raw_path); |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
Calculates the resized image dimensions according to the C<$self-Emax> |
|
710
|
|
|
|
|
|
|
and C<$self-Ethumbs> gallery constraints. Accepts the utf8 decoded path |
|
711
|
|
|
|
|
|
|
and the raw path to the file to be worked on. Returns two empty strings if |
|
712
|
|
|
|
|
|
|
there is error reading the image and warns about the error. Returns filenames |
|
713
|
|
|
|
|
|
|
for the resized image and the thumbnail image. See also |
|
714
|
|
|
|
|
|
|
L. |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
=head2 run |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
$обраꙁи = $обраꙁи->run(@ARGV); |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
Run this command. |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=head2 TEMPLATES |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
L contains four embedded |
|
725
|
|
|
|
|
|
|
templates: |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
obrazi.html — template for a single page gallery, containing all images |
|
728
|
|
|
|
|
|
|
obrazec.html — template for single image html pages |
|
729
|
|
|
|
|
|
|
obrazi.css — template for CSS rules used in both html templates |
|
730
|
|
|
|
|
|
|
obrazi.js — JavaScript (jQuery) code for handling navigation and effecs |
|
731
|
|
|
|
|
|
|
in obrazi.html |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
TODO: Maybe make the templates inflatable. |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
=head1 DEMOS |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
Here is a hopefully growing list of URLs of galleries produced with this |
|
738
|
|
|
|
|
|
|
software. |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
L<Творби на Марио Беров|https://слово.бг/хѫдожьство/mario_berov.bg.html> — a |
|
741
|
|
|
|
|
|
|
gallery, presenting works of the Bulgarian Orthodox icon painter Mario Berov. |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
L, L |
|
746
|
|
|
|
|
|
|
L, L, L, |
|
747
|
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
L, |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
L. |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=cut |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
__DATA__ |