return elem;
}
-function display_image(url, backend_width, backend_height, elem_id, offset, box)
+function display_image(url, backend_width, backend_height, elem_id, offset)
{
// See if this image already exists in the DOM; if not, add it.
var img = document.getElementById(elem_id);
img = document.createElement("img");
img.id = elem_id;
img.alt = "";
- img.className = box ? "fsbox" : "fsimg";
+ img.className = "fsimg";
}
img.style.position = "absolute";
img.style.transformOrigin = "top left";
if (offset === 0) {
img.src = url;
- position_image(img, backend_width, backend_height, offset, box);
+ position_image(img, backend_width, backend_height, offset, false);
} else {
// This is a preload, so wait for the main image to be ready.
// The test for .complete is an old IE hack, which I don't know if is relevant anymore.
if (main_img === null || main_img.complete) {
img.src = url;
} else {
- main_img.addEventListener('load', function() { img.src = url; }, false);
+ main_img.addEventListener('load', function() { img.src = url; }, { 'once': true });
}
// Seemingly one needs to delay position_image(), or Firefox will set the initial
// scroll offset completely off.
img.style.display = 'none';
setTimeout(function() {
- position_image(img, backend_width, backend_height, offset, box);
- img.style.display = null;
+ img.style.display = null; // Must be done before position_image(), for measurement.
+ position_image(img, backend_width, backend_height, offset, false);
+ }, 1);
+ }
+}
+
+function display_infobox(html, backend_width, backend_height, elem_id, offset)
+{
+ // See if this image already exists in the DOM; if not, add it.
+ var box = document.getElementById(elem_id);
+ if (box === null) {
+ box = document.createElement("div");
+ box.id = elem_id;
+ box.alt = "";
+ box.className = "fsbox";
+ }
+ box.style.position = "absolute";
+ box.style.transformOrigin = "top left";
+ box.innerHTML = html;
+ document.getElementById("main").appendChild(box);
+
+ if (offset === 0) {
+ position_image(box, backend_width, backend_height, offset, true);
+ } else {
+ // This is a preload.
+ // Seemingly one needs to delay position_image(), or Firefox will set the initial
+ // scroll offset completely off.
+ box.style.display = 'none';
+ setTimeout(function() {
+ box.style.display = null; // Must be done before position_image(), for measurement.
+ position_image(box, backend_width, backend_height, offset, true);
}, 1);
}
}
var url = window.location.origin + "/" + evt + "/" + backend_width + "x" + backend_height + "/" + filename;
var elem_id = num;
- display_image(url, adjusted_size[2], adjusted_size[3], elem_id, offset, false);
+ display_image(url, adjusted_size[2], adjusted_size[3], elem_id, offset);
if (global_infobox) {
- var url;
- var dpr = find_dpr();
var elem_id = num + "_box";
- if (dpr == 1) {
- url = window.location.origin + "/" + evt + "/" + backend_width + "x" + backend_height + "/box/" + filename;
- } else {
- url = window.location.origin + "/" + evt + "/" + backend_width + "x" + backend_height + "@" + dpr.toFixed(2) + "/box/" + filename;
- }
- display_image(url, adjusted_size[2], adjusted_size[3], elem_id, offset, true);
- document.getElementById(elem_id).style.transform += " scale(" + (1.0 / dpr) + ")";
+ display_infobox(global_image_list[num][4], adjusted_size[2], adjusted_size[3], elem_id, offset);
}
if (offset === 0) {
img.style.transform = "translate(" + extra_x_offset + "px,0px)";
if (box) {
- img.style.top = Math.min(top + height, screen_size[1] - 24) / dpr + "px";
+ img.style.top = Math.min(top + height, screen_size[1] - 24 * dpr) / dpr + "px";
+ img.style.height = "24px";
+ img.style.width = null;
+ img.style.whiteSpace = 'nowrap';
+ // Hide the box if there's no room for all the text.
+ img.style.opacity = (img.clientWidth < width / dpr + 10) ? null : 0.0;
+ img.style.width = (width / dpr) + "px";
} else {
img.style.top = (top / dpr) + "px";
img.style.lineHeight = (height / dpr) + "px";
// inum !== global_image_num + 1) {
// to_remove.push(child);
//}
- if (inum !== global_image_num) {
+ if (inum !== global_image_num || (child.className === "fsbox" && !global_infobox)) {
to_remove.push(child);
}
}
var inum = parseInt(child.id.replace("_box", ""));
var offset = inum - global_image_num;
child.style.transition = transition;
- child.style.transform = "translate(" + (x + find_width()[0] * offset / dpr) + "px,0px) scale(" + (1.0 / dpr) + ")";
+ child.style.transform = "translate(" + (x + find_width()[0] * offset / dpr) + "px,0px)";
}
}
}
use URI::Escape;
use File::Basename;
use Crypt::Eksblowfish::Bcrypt;
+use File::Temp;
BEGIN {
use Exporter ();
sub get_disk_location {
my ($r, $id) = @_;
- my $dir = POSIX::floor($id / 256);
+ my $dir = POSIX::floor($id / 256);
return $Sesse::pr0n::Config::image_base . "images/$dir/$id.jpg";
}
sub get_cache_location {
- my ($id, $width, $height) = @_;
- my $dir = POSIX::floor($id / 256);
-
- return $Sesse::pr0n::Config::image_base . "cache/$dir/$id-$width-$height-nobox.jpg";
-}
-
-sub get_infobox_cache_location {
- my ($id, $width, $height, $dpr) = @_;
- my $dir = POSIX::floor($id / 256);
+ my ($id, $width, $height, $format) = @_;
+ my $dir = POSIX::floor($id / 256);
- if ($dpr == 1) {
- return $Sesse::pr0n::Config::image_base . "cache/$dir/$id-$width-$height-box.png";
- } else {
- return $Sesse::pr0n::Config::image_base . "cache/$dir/$id-$width-$height-box\@$dpr.png";
- }
+ return $Sesse::pr0n::Config::image_base . "cache/$dir/$id-$width-$height-nobox.$format";
}
sub ensure_disk_location_exists {
my $img_dir = $Sesse::pr0n::Config::image_base . "/images/$dir/";
if (! -d $img_dir) {
log_info($r, "Need to create new image directory $img_dir");
- mkdir($img_dir) or die "Couldn't create new image directory $img_dir";
+ mkdir($img_dir); # Ignore errors, there could be a race.
+ -d $img_dir or die "Couldn't create new image directory $img_dir";
}
my $cache_dir = $Sesse::pr0n::Config::image_base . "/cache/$dir/";
if (! -d $cache_dir) {
log_info($r, "Need to create new cache directory $cache_dir");
- mkdir($cache_dir) or die "Couldn't create new image directory $cache_dir";
+ mkdir($cache_dir); # Ignore errors, there could be a race.
+ -d $cache_dir or die "Couldn't create new cache directory $cache_dir";
}
}
sub get_mipmap_location {
my ($r, $id, $width, $height) = @_;
- my $dir = POSIX::floor($id / 256);
+ my $dir = POSIX::floor($id / 256);
return $Sesse::pr0n::Config::image_base . "cache/$dir/$id-mipmap-$width-$height.jpg";
}
}
my $err;
- # ImageMagick can handle NEF files, but it does it by calling dcraw as a delegate.
- # The delegate support is rather broken and causes very odd stuff to happen when
- # more than one thread does this at the same time. Thus, we simply do it ourselves.
if ($filename =~ /\.(nef|cr2)$/i) {
- # this would suffice if ImageMagick gets to fix their handling
- # $physical_fname = "NEF:$physical_fname";
-
- open DCRAW, "-|", "dcraw", "-w", "-c", $physical_fname
- or error("dcraw: $!");
- $err = $magick->Read(file => \*DCRAW);
- close(DCRAW);
+ $physical_fname = "NEF:$physical_fname";
+ $err = $magick->Read($physical_fname);
} else {
# We always want YCbCr JPEGs. Setting this explicitly here instead of using
# RGB is slightly faster (no colorspace conversion needed) and works equally
}
sub ensure_cached {
- my ($r, $filename, $id, $dbwidth, $dbheight, $xres, $yres, @otherres) = @_;
-
- my ($new_dbwidth, $new_dbheight);
+ my ($r, $avif_ok, $jxl_ok, $filename, $id, $dbwidth, $dbheight, $xres, $yres, @otherres) = @_;
my $fname = get_disk_location($r, $id);
unless (defined($xres) && (!defined($dbwidth) || !defined($dbheight) || $xres < $dbwidth || $yres < $dbheight || $xres == -1)) {
return ($fname, undef);
}
- my $cachename = get_cache_location($id, $xres, $yres);
- my $err;
+ # See if we have an up-to-date JPEG-XL or AVIF to serve.
+ # (We never generate them on-the-fly, since they're so slow.)
+ my $cachename = get_cache_location($id, $xres, $yres, 'jxl');
+ if ($jxl_ok && -r $cachename and (-M $cachename <= -M $fname)) {
+ return ($cachename, 'image/jxl');
+ }
+
+ $cachename = get_cache_location($id, $xres, $yres, 'avif');
+ if ($avif_ok && -r $cachename and (-M $cachename <= -M $fname)) {
+ return ($cachename, 'image/avif');
+ }
+
+ $cachename = get_cache_location($id, $xres, $yres, 'jpg');
if (! -r $cachename or (-M $cachename > -M $fname)) {
# If we are in overload mode (aka Slashdot mode), refuse to generate
# new thumbnails.
error($r, 'System is in overload mode, not doing any scaling');
}
- my $img;
- ($img, $new_dbwidth, $new_dbheight) = make_mipmap($r, $filename, $id, $dbwidth, $dbheight, $xres, $yres, @otherres);
-
- while (defined($xres) && defined($yres)) {
- my ($nxres, $nyres) = (shift @otherres, shift @otherres);
- my $cachename = get_cache_location($id, $xres, $yres);
-
- my $cimg;
- if (defined($nxres) && defined($nyres)) {
- # we have more resolutions to scale, so don't throw
- # the image away
- $cimg = $img->Clone();
- } else {
- $cimg = $img;
- }
-
- my $width = $img->Get('columns');
- my $height = $img->Get('rows');
- my ($nwidth, $nheight) = scale_aspect($width, $height, $xres, $yres);
-
- my $filter = 'Lanczos';
- my $quality = 87;
- my $sf = "1x1";
-
- if ($xres != -1) {
- $cimg->Resize(width=>$nwidth, height=>$nheight, filter=>$filter, 'sampling-factor'=>$sf);
- }
-
- # Strip EXIF tags etc.
- $cimg->Strip();
+ make_cache($r, $filename, $id, $dbwidth, $dbheight, 'jpg', $xres, $yres, @otherres);
+ }
- {
- my %parms = (
- filename => $cachename,
- quality => $quality
- );
- if (($nwidth >= 640 && $nheight >= 480) ||
- ($nwidth >= 480 && $nheight >= 640)) {
- $parms{'interlace'} = 'Plane';
- }
- if (defined($sf)) {
- $parms{'sampling-factor'} = $sf;
- }
- $err = $cimg->write(%parms);
- }
+ return ($cachename, 'image/jpeg');
+}
- undef $cimg;
+sub make_cache {
+ my ($r, $filename, $id, $dbwidth, $dbheight, $format, $xres, $yres, @otherres) = @_;
- ($xres, $yres) = ($nxres, $nyres);
+ my ($img, $new_dbwidth, $new_dbheight) = make_mipmap($r, $filename, $id, $dbwidth, $dbheight, $xres, $yres, @otherres);
- log_info($r, "New cache: $nwidth x $nheight for $id.jpg");
- }
-
- undef $img;
- if ($err) {
- log_warn($r, "$fname: $err");
- $err =~ /(\d+)/;
- if ($1 >= 400) {
- #@$magick = ();
- error($r, "$fname: $err");
- }
- }
- }
-
# Update the SQL database if it doesn't contain the required info
if (!defined($dbwidth) && defined($new_dbwidth)) {
log_info($r, "Updating width/height for $id: $new_dbwidth x $new_dbheight");
update_image_info($r, $id, $new_dbwidth, $new_dbheight);
}
- return ($cachename, 'image/jpeg');
-}
-
-sub ensure_infobox_cached {
- my ($r, $filename, $id, $dbwidth, $dbheight, $dpr, $xres, $yres) = @_;
+ my $err;
+ while (defined($xres) && defined($yres)) {
+ my ($nxres, $nyres) = (shift @otherres, shift @otherres);
+ my $cachename = get_cache_location($id, $xres, $yres, $format);
+
+ my $cimg;
+ if (defined($nxres) && defined($nyres)) {
+ # we have more resolutions to scale, so don't throw
+ # the image away
+ $cimg = $img->Clone();
+ } else {
+ $cimg = $img;
+ }
+
+ my $width = $img->Get('columns');
+ my $height = $img->Get('rows');
+ my ($nwidth, $nheight) = scale_aspect($width, $height, $xres, $yres);
- my ($new_dbwidth, $new_dbheight);
+ my $filter = 'Lanczos';
+ my $quality = 87;
+ my $sf = "1x1";
- my $fname = get_disk_location($r, $id);
- my $cachename = get_infobox_cache_location($id, $xres, $yres, $dpr);
- my $err;
- if (! -r $cachename or (-M $cachename > -M $fname)) {
- # If we are in overload mode (aka Slashdot mode), refuse to generate
- # new thumbnails.
- if (Sesse::pr0n::Overload::is_in_overload($r)) {
- log_warn($r, "In overload mode, not scaling $id to $xres x $yres");
- error($r, 'System is in overload mode, not doing any scaling');
+ if ($xres != -1) {
+ $cimg->Resize(width=>$nwidth, height=>$nheight, filter=>$filter);
}
- # We need the exact width so we can make one in the right size.
- my ($width, $height);
+ # Strip EXIF tags etc.
+ $cimg->Strip();
- # This is slow, but should fortunately almost never happen, so don't bother
- # special-casing it.
- if (!defined($dbwidth) || !defined($dbheight)) {
- my $img = read_original_image($r, $filename, $id, $dbwidth, $dbheight, 0);
- $new_dbwidth = $width = $img->Get('columns');
- $new_dbheight = $height = $img->Get('rows');
+ if ($format eq 'jpg') {
+ my %parms = (
+ filename => $cachename,
+ quality => $quality
+ );
+ if (($nwidth >= 640 && $nheight >= 480) ||
+ ($nwidth >= 480 && $nheight >= 640)) {
+ $parms{'interlace'} = 'Plane';
+ }
+ if (defined($sf)) {
+ $parms{'sampling-factor'} = $sf;
+ }
+ $err = $cimg->write(%parms);
+ } elsif ($format eq 'avif') {
+ # ImageMagick doesn't have AVIF support until version 7,
+ # and Debian hasn't packaged that even in unstable as of 2021.
+ # So we'll need to do it the manual way. (We don't use /tmp, for security reasons.)
+ (my $dirname = $cachename) =~ s,/[^/]*$,,;
+ my ($fh, $raw_filename) = File::Temp::tempfile('tmp.XXXXXXXX', DIR => $dirname, SUFFIX => '.ycbcr');
+ # Write a Y4M header, so that we get the chroma range correct.
+ printf $fh "YUV4MPEG2 W%d H%d F25:1 Ip A1:1 C444 XYSCSS=444 XCOLORRANGE=FULL\nFRAME\n", $nwidth, $nheight;
+ my %parms = (
+ file => $fh,
+ filename => $raw_filename,
+ interlace => 'Plane'
+ );
+ $cimg->write(%parms);
+ close($fh);
+ my $ivf_filename;
+ ($fh, $ivf_filename) = File::Temp::tempfile('tmp.XXXXXXXX', DIR => $dirname, SUFFIX => '.ivf');
+ close($fh);
+ system('aomenc', '--quiet', '--cpu-used=0', '--bit-depth=10', '--end-usage=q', '--cq-level=13', '--target-bitrate=0', '--good', '--aq-mode=1', '--matrix-coefficients=bt601', '-o', $ivf_filename, $raw_filename);
+ unlink($raw_filename);
+ system('MP4Box', '-quiet', '-add-image', "$ivf_filename:primary", '-ab', 'avif', '-ab', 'miaf', '-new', $cachename);
+ unlink($ivf_filename);
+ } elsif ($format eq 'jxl') {
+ # Similar, for JPEG-XL.
+ (my $dirname = $cachename) =~ s,/[^/]*$,,;
+ my ($fh, $raw_filename) = File::Temp::tempfile('tmp.XXXXXXXX', DIR => $dirname, SUFFIX => '.ppm');
+ my %parms = (
+ file => $fh,
+ filename => $raw_filename
+ );
+ $cimg->write(%parms);
+ close($fh);
+ system('cjxl', '-p', $raw_filename, $cachename);
+ unlink($raw_filename);
} else {
- $width = $dbwidth;
- $height = $dbheight;
+ die "Unknown format $format";
}
- my $img = Image::Magick->new;
- if (defined($xres) && defined($yres)) {
- ($width, $height) = scale_aspect($width, $height, $xres, $yres);
- }
- $height = 24 * $dpr;
- $img->Set(size=>($width . "x" . $height));
- $img->Read('xc:white');
-
- my $info = Image::ExifTool::ImageInfo($fname);
- if (make_infobox($img, $info, $r, $dpr)) {
- $img->Quantize(colors=>16, dither=>'False');
-
- # Since the image is grayscale, ImageMagick overrides us and writes this
- # as grayscale anyway, but at least we get rid of the alpha channel this
- # way.
- $img->Set(type=>'Palette');
- } else {
- # Not enough room for the text, make a tiny dummy transparent infobox
- @$img = ();
- $img->Set(size=>"1x1");
- $img->Read('null:');
+ undef $cimg;
- $width = 1;
- $height = 1;
- }
+ ($xres, $yres) = ($nxres, $nyres);
- $err = $img->write(filename => $cachename, quality => 90, depth => 8);
- log_info($r, "New infobox cache: $width x $height for $id.jpg");
+ log_info($r, "New cache: $nwidth x $nheight ($format) for $id");
+ }
+
+ undef $img;
+ if ($err) {
+ log_warn($r, "$filename: $err");
+ $err =~ /(\d+)/;
+ if ($1 >= 400) {
+ #@$magick = ();
+ error($r, "$filename: $err");
+ }
}
-
- return ($cachename, 'image/png');
}
sub get_mimetype_from_filename {
return $type;
}
-sub make_infobox {
- my ($img, $info, $r, $dpr) = @_;
+sub make_infobox_parts {
+ my ($info) = @_;
# The infobox is of the form
# "Time - date - focal length, shutter time, aperture, sensitivity, exposure bias - flash",
}
}
- return 0 if (scalar @parts == 0);
-
- # Find the required width
- my $th = 0;
- my $tw = 0;
-
- for my $part (@parts) {
- my $font;
- if ($part->[1]) {
- $font = '/usr/share/fonts/truetype/msttcorefonts/Arial_Bold.ttf';
- } else {
- $font = '/usr/share/fonts/truetype/msttcorefonts/Arial.ttf';
- }
-
- my (undef, undef, $h, undef, $w) = ($img->QueryFontMetrics(text=>$part->[0], font=>$font, pointsize=>12*$dpr));
-
- $tw += $w;
- $th = $h if ($h > $th);
- }
-
- return 0 if ($tw > $img->Get('columns'));
-
- my $x = 0;
- my $y = $img->Get('rows') - 24*$dpr;
-
- # Hit exact DCT blocks
- $y -= ($y % 8);
-
- my $points = sprintf "%u,%u %u,%u", $x, $y, ($img->Get('columns') - 1), ($img->Get('rows') - 1);
- my $lpoints = sprintf "%u,%u %u,%u", $x, $y, ($img->Get('columns') - 1), $y;
- $img->Draw(primitive=>'rectangle', stroke=>'white', fill=>'white', points=>$points);
- $img->Draw(primitive=>'line', stroke=>'black', strokewidth=>$dpr, points=>$lpoints);
-
- # Start writing out the text
- $x = ($img->Get('columns') - $tw) / 2;
-
- my $room = ($img->Get('rows') - $dpr - $y - $th);
- $y = ($img->Get('rows') - $dpr) - $room/2;
-
- for my $part (@parts) {
- my $font;
- if ($part->[1]) {
- $font = '/usr/share/fonts/truetype/msttcorefonts/Arial_Bold.ttf';
- } else {
- $font = '/usr/share/fonts/truetype/msttcorefonts/Arial.ttf';
- }
- $img->Annotate(text=>$part->[0], font=>$font, pointsize=>12*$dpr, x=>int($x), y=>int($y));
- $x += ($img->QueryFontMetrics(text=>$part->[0], font=>$font, pointsize=>12*$dpr))[4];
- }
-
- return 1;
+ return @parts;
}
sub gcd {
push @ret, "/$event/$1x$2/$filename";
} elsif ($fname =~ /^$id-(\d+)-(\d+)-nobox\.jpg$/) {
push @ret, "/$event/$1x$2/nobox/$filename";
- } elsif ($fname =~ /^$id--1--1-box\.png$/) {
- push @ret, "/$event/box/$filename";
- } elsif ($fname =~ /^$id-(\d+)-(\d+)-box\.png$/) {
- push @ret, "/$event/$1x$2/box/$filename";
} else {
log_warn($r, "Couldn't find a purging URL for $fname");
}
sub log_info {
my ($r, $msg) = @_;
- if (defined($r->logger)) {
+ if (defined($r->{'logger'})) {
$r->logger->({ level => 'info', message => $msg });
} else {
print STDERR "[INFO] $msg\n";
sub log_warn {
my ($r, $msg) = @_;
- if (defined($r->logger)) {
+ if (defined($r->{'logger'})) {
$r->logger->({ level => 'warn', message => $msg });
} else {
print STDERR "[WARN] $msg\n";
sub log_error {
my ($r, $msg) = @_;
- if (defined($r->logger)) {
+ if (defined($r->{'logger'})) {
$r->logger->({ level => 'error', message => $msg });
} else {
print STDERR "[ERROR] $msg\n";
my $where;
if ($event eq '+all') {
- $where = '';
+ $where = ' AND (event,vhost) IN ( SELECT event,vhost FROM events WHERE NOT hidden )';
} else {
$where = ' AND event=' . $dbh->quote($event);
}
# Find all images related to this event.
my $limit = (defined($start) && defined($num) && !$settings{'fullscreen'}) ? (" LIMIT $num OFFSET " . ($start-1)) : "";
- my $q = $dbh->prepare("SELECT *, (date - INTERVAL '6 hours')::date AS day FROM images WHERE vhost=? $where AND NOT is_render ORDER BY (date - INTERVAL '6 hours')::date $datesort,takenby,date,filename $limit")
+ my $extra_joins = "";
+ my $extra_fields = "";
+ if ($settings{'fullscreen'}) {
+ $extra_joins = <<"EOF";
+ LEFT JOIN exif_info exif_prog ON images.id=exif_prog.image AND exif_prog.key = 'ExposureProgram'
+ LEFT JOIN exif_info exif_focal ON images.id=exif_focal.image AND exif_focal.key = 'FocalLength'
+ LEFT JOIN exif_info exif_shutter ON images.id=exif_shutter.image AND exif_shutter.key = 'ExposureTime'
+ LEFT JOIN exif_info exif_fnum ON images.id=exif_fnum.image AND exif_fnum.key = 'FNumber'
+ LEFT JOIN exif_info exif_iso1 ON images.id=exif_iso1.image AND exif_iso1.key = 'ISO'
+ LEFT JOIN exif_info exif_iso2 ON images.id=exif_iso2.image AND exif_iso2.key = 'ISOSetting'
+ LEFT JOIN exif_info exif_ev1 ON images.id=exif_ev1.image AND exif_ev1.key = 'ExposureBiasValue'
+ LEFT JOIN exif_info exif_ev2 ON images.id=exif_ev2.image AND exif_ev2.key = 'ExposureCompensation'
+ LEFT JOIN exif_info exif_date ON images.id=exif_date.image AND exif_date.key = 'DateTimeOriginal'
+ LEFT JOIN exif_info exif_model ON images.id=exif_model.image AND exif_model.key = 'Model'
+ LEFT JOIN exif_info exif_flash ON images.id=exif_flash.image AND exif_flash.key = 'Flash'
+EOF
+ $extra_fields = <<"EOF";
+ exif_prog.value AS "ExposureProgram",
+ exif_focal.value AS "FocalLength",
+ exif_shutter.value AS "ExposureTime",
+ exif_fnum.value AS "FNumber",
+ exif_iso1.value AS "ISO",
+ exif_iso2.value AS "ISOSetting",
+ exif_ev1.value AS "ExposureBiasValue",
+ exif_ev2.value AS "ExposureCompensation",
+ exif_date.value AS "DateTimeOriginal",
+ exif_model.value AS "Model",
+ exif_flash.value AS "Flash",
+EOF
+ }
+
+ my $q = $dbh->prepare(<<"EOF")
+SELECT *,
+ $extra_fields
+ (date - INTERVAL '6 hours')::date AS day
+FROM
+ images
+ $extra_joins
+WHERE
+ vhost=?
+ $where
+ AND NOT is_render
+ORDER BY (date - INTERVAL '6 hours')::date $datesort,takenby,date,filename
+$limit
+EOF
or return dberror($r, "prepare()");
$q->execute(Sesse::pr0n::Common::get_server_name($r))
or return dberror($r, "image enumeration");
while (my $ref = $q->fetchrow_hashref()) {
my $width = defined($ref->{'width'}) ? $ref->{'width'} : -1;
my $height = defined($ref->{'height'}) ? $ref->{'height'} : -1;
- push @files, [ $ref->{'event'}, $ref->{'filename'}, $width, $height ];
+ my @parts = Sesse::pr0n::Common::make_infobox_parts($ref);
+ @parts = map { $_->[1] ? "<strong>" . HTML::Entities::encode_entities($_->[0]) . "</strong>" : HTML::Entities::encode_entities($_->[0]) } @parts;
+ push @files, [ $ref->{'event'}, $ref->{'filename'}, $width, $height, join('', @parts) ];
}
for my $i (0..$#files) {
- my $line = sprintf " [ \"%s\", \"%s\", %d, %d ]", @{$files[$i]};
+ my $line = sprintf " [ \"%s\", \"%s\", %d, %d, \"%s\" ]", @{$files[$i]};
$line .= "," unless ($i == $#files);
$io->print($line . "\n");
}