package LDV::Imgbin; use strict; use warnings; use utf8; use Mojo::Base 'Mojolicious::Controller'; use JSON qw(encode_json decode_json); use Mojo::Asset::File; use Imager; sub _data_path { my ($self, $time) = @_; my $root = $self->app->config->{imgbin}->{root}; return $self->app->home->rel_file("$root/$time.json"); } sub _image_path { my ($self, $time, $ext, $full) = @_; return "/images/full/$time.$ext" unless $full; return $self->app->home->rel_file("public/images/full/$time.$ext"); } sub _thumb_path { my ($self, $time, $full) = @_; my $ext = $self->app->config->{imgbin}->{thumbs_ext}; return "/images/small/$time.$ext" unless $full; return $self->app->home->rel_file("public/images/small/$time.$ext"); } sub create { my ($self) = @_; $self->stash(maxsize => $self->config->{imgbin}->{maxsize}); $self->render(); } sub view { my ($self) = @_; my $time = $self->stash('time') || 0; eval { my $path = $self->_data_path($time); my $data = Mojo::Asset::File->new(path => $path); my $json = decode_json($data->slurp); die("image expired\n") if (time() > $json->{expire}); $json->{path} = $self->_image_path($time, $json->{format}); $self->stash(image => $json); 1; $self->render(); 1; } or do { $self->flash(error => $@); $self->redirect_to('/imgbin'); }; $self->rendered(); } sub save { my ($self) = @_; eval { die ("request too large\n") if $self->req->is_limit_exceeded; my $time = time(); my $conf = $self->app->config->{imgbin}; my $upload = $self->req->upload('file'); my $expire = $self->req->param('expire') || 7; # days die("no file uploaded\n") unless ($upload and $upload->size > 0); die("file too large\n") if ($upload->size > $conf->{maxsize}); # hack: don't use memory backend { my $path = POSIX::tmpnam; $upload->move_to($path); $upload->asset(Mojo::Asset::File->new(path => $path)); $upload->asset->cleanup(1); # rearm self-destruction } my ($im_w, $im_h) = ($conf->{maxres} =~ m/(\d+)x(\d+)/oi); Imager->set_file_limits( width => $im_w, height => $im_h, bytes => $conf->{maxmem}, ); my $image = Imager->new(file => $upload->asset->path) or die(Imager->errstr() . "\n"); my $json = { comment => $image->tags(name => 'i_comment') // '', format => $image->tags(name => 'i_format'), expire => $time + $expire * 86400, # expired *after* this time width => $image->getwidth, height => $image->getheight, name => $upload->filename, size => $upload->size, }; my ($th_w, $th_h) = ($conf->{thumbs_size} =~ m/(\d+)x(\d+)/oi); my $thumb = $image->scale(xpixels => $th_w, ypixels => $th_h, type => 'min'); $thumb->write(file => $self->_thumb_path($time, 'fullpath')) or die $thumb->errstr; $upload->move_to($self->_image_path($time, $json->{format}, 'fullpath')); my $data = Mojo::Asset::File->new(path => $self->_data_path($time)); $data->add_chunk(encode_json($json)); $data->move_to($self->_data_path($time)); $self->redirect_to("/imgbin/$time"); } or do { $self->app->log->error($@); $@ =~ s/\s+ at \s+ .+ \s+ line \s+ \d+//oxi; $self->flash(error => $@); $self->redirect_to('/imgbin'); }; $self->rendered(); } sub latest { my ($self) = @_; eval { my @images = (); my $root = $self->app->config->{imgbin}->{root}; opendir(my $DH, $root) or die("opendir: $!\n"); my @items = grep { m/\d+\.json/oi } readdir($DH); closedir($DH); @items = map { m/(\d+)\.json/oi } @items; @items = sort { $b <=> $a } @items; foreach my $time (splice(@items, 0, 10)) { my $data = Mojo::Asset::File->new(path => $self->_data_path($time)); my $image = decode_json($data->slurp); $image->{path} = $self->_thumb_path($time); $image->{url} = sprintf '/imgbin/%d', $time; push @images, $image; } $self->stash(images => \@images); $self->render(); } or do { $self->flash(error => $@); $self->redirect_to('/imgbin'); }; } sub prune { my ($self) = @_; eval { my $root = $self->app->config->{imgbin}->{root}; opendir(my $DH, $root) or die("opendir: $!\n"); my @files = grep { m/\d+\.json/oi } readdir($DH); my @times = map { s/\.json//oi; $_ } @files; closedir($DH); my $now = time(); foreach my $time (@times) { my $data = Mojo::Asset::File->new(path => $self->_data_path($time)); my $image = decode_json($data->slurp); next if $image->{expire} > $now; unlink $self->_image_path($time, $image->{format}, 1); unlink $self->_thumb_path($time); } 1; } or do { chomp $@; $self->app->log->error($@); $@ =~ s/\s+ at \s+ .+ \s+ line \s+ \d+//oxi; $self->flash(error => $@); }; $self->redirect_to('/imgbin'); $self->rendered(); } 1;