From d4c4fb3208caa6a12a54f670afa7b0e2d96aa3ad Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Tue, 26 Jun 2018 17:36:20 +0200 Subject: support recursive thumbnail generation on directory trees --- bin/dthumb | 5 +- lib/App/Dthumb.pm | 106 ++++++++++++++++++++++++++---------------- share/html_start.dthumb | 10 ++-- t/20-app-dthumb.t | 4 +- t/21-app-dthumb.files.t | 23 ++++----- t/cmp/index.names | 4 +- t/cmp/index.no-names | 4 +- t/imgdir/.thumbs/invalid.png | 0 t/imgdir/no_image | 0 t/imgdir/one.png | Bin 6525 -> 0 bytes t/imgdir/two.png | Bin 13552 -> 0 bytes t/out/.thumbnails/invalid.png | 0 t/out/no_image | 0 t/out/one.png | Bin 0 -> 6525 bytes t/out/two.png | Bin 0 -> 13552 bytes 15 files changed, 92 insertions(+), 64 deletions(-) delete mode 100644 t/imgdir/.thumbs/invalid.png delete mode 100644 t/imgdir/no_image delete mode 100644 t/imgdir/one.png delete mode 100644 t/imgdir/two.png create mode 100644 t/out/.thumbnails/invalid.png create mode 100644 t/out/no_image create mode 100644 t/out/one.png create mode 100644 t/out/two.png diff --git a/bin/dthumb b/bin/dthumb index 32931f9..2f6a718 100755 --- a/bin/dthumb +++ b/bin/dthumb @@ -96,8 +96,6 @@ current directory. It uses a javascript lightbox (see L) to display images. To view them without the lightbox, open images by clicking on the filename instead of the thumbnail. -Note that recursion is not yet supported. - During operation, B will show its progress on STDERR. =head1 OPTIONS @@ -118,7 +116,8 @@ Recreate all thumbnails =item B<-R>, B<--recursive> -Also include directories in the file listing +Include directories in file listing and recurnsively generate thumbnails +for their contents. =item B<-d>, B<--size> I (default: 200) diff --git a/lib/App/Dthumb.pm b/lib/App/Dthumb.pm index 0223016..3d999bc 100755 --- a/lib/App/Dthumb.pm +++ b/lib/App/Dthumb.pm @@ -26,8 +26,8 @@ sub new { $conf{file_index} //= 'index.html'; $conf{dir_images} //= q{.}; - $conf{dir_data} = "$conf{dir_images}/.dthumb"; - $conf{dir_thumbs} = "$conf{dir_images}/.thumbs"; + $conf{dir_data} = "$conf{dir_images}/.dthumb"; + $conf{suffix_thumbs} = '.thumbnails'; $conf{names} //= ( $conf{'no-names'} ? 0 : 1 ); @@ -43,45 +43,53 @@ sub new { height => $conf{size} * $conf{spacing} . 'px', ); - $ref->{html} = $ref->{data}->get('html_start.dthumb'); - return bless( $ref, $obj ); } sub read_directories { my ($self) = @_; - my $thumbdir = $self->{config}->{dir_thumbs}; + my $thumbdir = $self->{config}->{suffix_thumbs}; my $imgdir = $self->{config}->{dir_images}; my ( @files, @old_thumbs ); - for my $file ( read_dir($imgdir) ) { + my @queue = read_dir( $imgdir, prefix => 1 ); + my @paths = ($imgdir); + + for my $path (@queue) { + my ( $basedir, $file ) = ( $path =~ m{ ^ (.*) / ([^/]*) $ }x ); if ( $file =~ m{ ^ [.] }x ) { next; } if ( $file eq 'index.html' ) { next; } - if ( -f "${imgdir}/${file}" + if ( -f $path and ( $self->{config}{all} or $file =~ m{ [.] (png | jp e? g) $ }ix ) ) { - push( @files, $file ); + push( @files, $path ); } - elsif ( $self->{config}{recursive} and -d "${imgdir}/${file}" ) { - push( @files, $file ); + elsif ( $self->{config}{recursive} and -d $path ) { + push( @files, $path ); + push( @queue, read_dir( $path, prefix => 1 ) ); + push( @paths, $path ); } } - if ( -d $thumbdir ) { - for my $file ( read_dir($thumbdir) ) { - if ( $file =~ m{^ [^.] }ox and not -f "${imgdir}/${file}" ) { - push( @old_thumbs, $file ); + for my $path (@paths) { + if ( -d "${path}/${thumbdir}" ) { + for my $file ( read_dir("${path}/${thumbdir}") ) { + if ( $file =~ m{^ [^.] }ox and not -f "${path}/${file}" ) { + push( @old_thumbs, "${path}/$file" ); + } } } + $self->{html}->{$path} = $self->{data}->get('html_start.dthumb'); } @{ $self->{files} } = sort { lc($a) cmp lc($b) } @files; + @{ $self->{paths} } = sort { lc($a) cmp lc($b) } @paths; @{ $self->{old_thumbnails} } = @old_thumbs; return; @@ -90,7 +98,7 @@ sub read_directories { sub create_files { my ($self) = @_; - my $thumbdir = $self->{config}->{dir_thumbs}; + my $thumbdir = $self->{config}->{suffix_thumbs}; my $datadir = $self->{config}->{dir_data}; my @files = $self->{data}->list_archived; my @icons; @@ -102,12 +110,18 @@ sub create_files { push( @icons, 'places/folder-blue.png' ); } - for my $dir ( $thumbdir, $datadir, "${datadir}/css", "${datadir}/js" ) { + for my $dir ( $datadir, "${datadir}/css", "${datadir}/js" ) { if ( not -d $dir ) { mkdir($dir); } } + for my $path ( @{ $self->{paths} } ) { + if ( not -d "${path}/${thumbdir}" ) { + mkdir("${path}/${thumbdir}"); + } + } + for my $file (@files) { write_file( "${datadir}/${file}", $self->{data}->get($file) ); } @@ -124,10 +138,8 @@ sub create_files { sub delete_old_thumbnails { my ($self) = @_; - my $thumbdir = $self->{config}->{dir_thumbs}; - for my $file ( @{ $self->{old_thumbnails} } ) { - unlink("${thumbdir}/${file}"); + unlink($file); } return; @@ -140,70 +152,77 @@ sub get_files { } sub create_thumbnail_html { - my ( $self, $file ) = @_; + my ( $self, $path ) = @_; my $div_width = $self->{config}->{size} * $self->{config}->{spacing}; my $div_height = $div_width + ( $self->{config}->{names} ? 10 : 0 ); - $self->{html} .= "
\n"; + my ( $basedir, $file ) = ( $path =~ m{ ^ (.*) / ([^/]*) $ }x ); - if ( -d $file ) { - $self->{html} .= sprintf( + my $html = \$self->{html}->{$basedir}; + + $$html .= "
\n"; + + if ( -d $path ) { + $$html .= sprintf( "\t\n" - . "\t\t\"%s\"\n", + . "\t\t.dthumb/folder-blue.png\" alt=\"%s\" />\n", ($file) x 3, ); } elsif ( $file =~ m{ [.] (png | jp e? g) $ }ix ) { - $self->{html} .= sprintf( + $$html .= sprintf( "\t\n" . "\t\t\"%s\"\n", ($file) x 2, - $self->{config}->{dir_thumbs}, + $self->{config}->{suffix_thumbs}, ($file) x 2, ); } else { - $self->{html} .= sprintf( + $$html .= sprintf( "\t\n" - . "\t\t\"%s\"\n", + . "\t\t.dthumb/unknown.png\" alt=\"%s\" />\n", ($file) x 3, ); } if ( $self->{config}->{names} or -d $file ) { - $self->{html} .= sprintf( + $$html .= sprintf( "\t
\n" . "\t%s\n", 'text-decoration: none', ($file) x 2, ); } - $self->{html} .= "
\n"; + $$html .= "
\n"; return; } sub create_thumbnail_image { - my ( $self, $file ) = @_; + my ( $self, $path ) = @_; - my $thumbdir = $self->{config}->{dir_thumbs}; + my $thumbdir = $self->{config}->{suffix_thumbs}; my $thumb_dim = $self->{config}->{size}; - if ( -e "${thumbdir}/${file}" + my ( $basedir, $file ) = ( $path =~ m{ ^ (.*) / ([^/]*) $ }x ); + + if ( -e "${basedir}/${thumbdir}/${file}" and not $self->{config}->{recreate} - and ( stat($file) )[9] <= ( stat("${thumbdir}/${file}") )[9] ) + and ( stat($path) )[9] + <= ( stat("${basedir}/${thumbdir}/${file}") )[9] ) { return; } - if ( -d $file + if ( -d $path or $self->{config}{all} and not( $file =~ m{ [.] (png | jp e? g) $ }ix ) ) { return; } - my $image = Image::Imlib2->load($file); + my $image = Image::Imlib2->load($path); my ( $dx, $dy ) = ( $image->width, $image->height ); my $thumb = $image; @@ -217,7 +236,7 @@ sub create_thumbnail_image { } $thumb->set_quality( $self->{config}->{quality} ); - $thumb->save("${thumbdir}/${file}"); + $thumb->save("${basedir}/${thumbdir}/${file}"); return; } @@ -225,9 +244,18 @@ sub create_thumbnail_image { sub write_out_html { my ($self) = @_; - $self->{html} .= $self->{data}->get('html_end.dthumb'); + my $index_name = $self->{config}->{file_index}; - write_file( $self->{config}->{file_index}, $self->{html} ); + for my $path ( @{ $self->{paths} } ) { + my $diff = substr( $path, length( $self->{config}->{dir_images} ) ); + my $path_to_base = q{}; + if ( length($diff) ) { + $path_to_base = '../' x ( scalar split( qr{/}, $diff ) - 1 ); + } + $self->{html}->{$path} .= $self->{data}->get('html_end.dthumb'); + $self->{html}->{$path} =~ s{}{$path_to_base}g; + write_file( "${path}/${index_name}", $self->{html}->{$path} ); + } return; } diff --git a/share/html_start.dthumb b/share/html_start.dthumb index e6ac830..0cdf5cc 100644 --- a/share/html_start.dthumb +++ b/share/html_start.dthumb @@ -4,11 +4,11 @@ <!-- $title --> - - - - - + + + + +