|
|
|
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');
|
|
|
|
|
|
|
|
eval {
|
|
|
|
my $path = $self->_data_path($time);
|
|
|
|
my $data = Mojo::Asset::File->new(path => $path);
|
|
|
|
$data->cleanup(0);
|
|
|
|
my $json = decode_json($data->slurp);
|
|
|
|
die("image expired\n") if (time() > $data->{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 {
|
|
|
|
$self->flash(error => $@);
|
|
|
|
};
|
|
|
|
$self->redirect_to('/imgbin');
|
|
|
|
$self->rendered();
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|