mirror of
https://github.com/openvk/openvk
synced 2024-12-22 16:42:32 +03:00
Add photo miniatures creation logic to Photo::saveFile
This commit is contained in:
parent
f707c72f07
commit
608c34815e
5 changed files with 170 additions and 14 deletions
|
@ -1,5 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use MessagePack\MessagePack;
|
||||||
use openvk\Web\Models\Entities\Album;
|
use openvk\Web\Models\Entities\Album;
|
||||||
use openvk\Web\Models\Repositories\Albums;
|
use openvk\Web\Models\Repositories\Albums;
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
@ -13,13 +14,73 @@ class Photo extends Media
|
||||||
|
|
||||||
const ALLOWED_SIDE_MULTIPLIER = 7;
|
const ALLOWED_SIDE_MULTIPLIER = 7;
|
||||||
|
|
||||||
|
private function resizeImage(string $filename, string $outputDir, \SimpleXMLElement $size): array
|
||||||
|
{
|
||||||
|
$res = [false];
|
||||||
|
$image = Image::fromFile($filename);
|
||||||
|
$requiresProportion = ((string) $size["requireProp"]) != "none";
|
||||||
|
if($requiresProportion) {
|
||||||
|
$props = explode(":", (string) $size["requireProp"]);
|
||||||
|
$px = (int) $props[0];
|
||||||
|
$py = (int) $props[1];
|
||||||
|
if(($image->getWidth() / $image->getHeight()) > ($px / $py)) {
|
||||||
|
# For some weird reason using resize with EXACT flag causes system to consume an unholy amount of RAM
|
||||||
|
$image->crop(0, 0, "100%", (int) ceil(($px * $image->getWidth()) / $py));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($size["maxSize"])) {
|
||||||
|
$maxSize = (int) $size["maxSize"];
|
||||||
|
$image->resize($maxSize, $maxSize, Image::SHRINK_ONLY | Image::FIT);
|
||||||
|
} else if(isset($size["maxResolution"])) {
|
||||||
|
$resolution = explode("x", (string) $size["maxResolution"]);
|
||||||
|
$image->resize((int) $resolution[0], (int) $resolution[1], Image::SHRINK_ONLY | Image::FIT);
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException("Malformed size description: " . (string) $size["id"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res[1] = $image->getWidth();
|
||||||
|
$res[2] = $image->getHeight();
|
||||||
|
if($res[1] <= 300 || $res[2] <= 300)
|
||||||
|
$image->save("$outputDir/" . (string) $size["id"] . ".gif");
|
||||||
|
else
|
||||||
|
$image->save("$outputDir/" . (string) $size["id"] . ".jpeg");
|
||||||
|
|
||||||
|
imagedestroy($image->getImageResource());
|
||||||
|
unset($image);
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function saveImageResizedCopies(string $filename, string $hash): void
|
||||||
|
{
|
||||||
|
$dir = dirname($this->pathFromHash($hash));
|
||||||
|
$dir = "$dir/$hash" . "_cropped";
|
||||||
|
if(!is_dir($dir))
|
||||||
|
mkdir($dir);
|
||||||
|
|
||||||
|
$sizes = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
|
||||||
|
if(!$sizes)
|
||||||
|
throw new \RuntimeException("Could not load photosizes.xml!");
|
||||||
|
|
||||||
|
$sizesMeta = [];
|
||||||
|
foreach($sizes->Size as $size)
|
||||||
|
$sizesMeta[(string) $size["id"]] = $this->resizeImage($filename, $dir, $size);
|
||||||
|
|
||||||
|
$sizesMeta = MessagePack::pack($sizesMeta);
|
||||||
|
$this->stateChanges("sizes", $sizesMeta);
|
||||||
|
}
|
||||||
|
|
||||||
protected function saveFile(string $filename, string $hash): bool
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
{
|
{
|
||||||
$image = Image::fromFile($filename);
|
$image = Image::fromFile($filename);
|
||||||
if(($image->height >= ($image->width * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($image->width >= ($image->height * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
if(($image->height >= ($image->width * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($image->width >= ($image->height * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
||||||
throw new ISE("Invalid layout: image is too wide/short");
|
throw new ISE("Invalid layout: image is too wide/short");
|
||||||
|
|
||||||
|
$image->resize(8192, 4320, Image::SHRINK_ONLY | Image::FIT);
|
||||||
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
||||||
|
$this->saveImageResizedCopies($filename, $hash);
|
||||||
|
$this->getDimensions(); # propagate dimensions info in DB
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -46,23 +107,98 @@ class Photo extends Media
|
||||||
DB::i()->getContext()->table("album_relations")->where("media", $this->getRecord()->id)->delete();
|
DB::i()->getContext()->table("album_relations")->where("media", $this->getRecord()->id)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDimensions(): array
|
function getSizes(): ?array
|
||||||
{
|
{
|
||||||
|
$sizes = $this->getRecord()->sizes;
|
||||||
|
if(!$sizes) {
|
||||||
|
if(OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["upgradeStructure"]) {
|
||||||
|
$hash = $this->getRecord()->hash;
|
||||||
|
$this->saveImageResizedCopies($this->pathFromHash($hash), $hash);
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $this->getSizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = [];
|
||||||
|
$sizes = MessagePack::unpack($sizes);
|
||||||
|
foreach($sizes as $id => $meta) {
|
||||||
|
$url = $this->getURL();
|
||||||
|
$url = str_replace(".$this->fileExtension", "_cropped/$id.", $url);
|
||||||
|
$url .= ($meta[1] <= 300 || $meta[2] <= 300) ? "gif" : "jpeg";
|
||||||
|
|
||||||
|
$res[$id] = (object) [
|
||||||
|
"url" => $url,
|
||||||
|
"width" => $meta[1],
|
||||||
|
"height" => $meta[2],
|
||||||
|
"crop" => $meta[0]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
[$x, $y] = $this->getDimensions();
|
||||||
|
$res["UPLOADED_MAXRES"] = (object) [
|
||||||
|
"url" => $this->getURL(),
|
||||||
|
"width" => $x,
|
||||||
|
"height" => $y,
|
||||||
|
"crop" => false
|
||||||
|
];
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVkApiSizes(): ?array
|
||||||
|
{
|
||||||
|
$res = [];
|
||||||
|
$sizes = $this->getSizes();
|
||||||
|
if(!$sizes)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$manifest = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
|
||||||
|
if(!$manifest)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$mappings = [];
|
||||||
|
foreach($manifest->Size as $size)
|
||||||
|
$mappings[(string) $size["id"]] = (string) $size["vkId"];
|
||||||
|
|
||||||
|
foreach($sizes as $id => $meta)
|
||||||
|
$res[$mappings[$id] ?? $id] = $meta;
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getURLBySizeId(string $size): string
|
||||||
|
{
|
||||||
|
$sizes = $this->getSizes();
|
||||||
|
if(!$sizes)
|
||||||
|
return $this->getURL();
|
||||||
|
|
||||||
|
$size = $sizes[$size];
|
||||||
|
if(!$size)
|
||||||
|
return $this->getURL();
|
||||||
|
|
||||||
|
return $size->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDimensions(): array
|
||||||
|
{
|
||||||
$x = $this->getRecord()->width;
|
$x = $this->getRecord()->width;
|
||||||
$y = $this->getRecord()->height;
|
$y = $this->getRecord()->height;
|
||||||
if(!$x) { # no sizes in database
|
if(!$x) { # no sizes in database
|
||||||
$hash = $this->getRecord()->hash;
|
$hash = $this->getRecord()->hash;
|
||||||
$image = new \Imagick($this->pathFromHash($hash));
|
$image = Image::fromFile($this->pathFromHash($hash));
|
||||||
|
|
||||||
$x = $image->getImageWidth();
|
$x = $image->getWidth();
|
||||||
$y = $image->getImageHeight();
|
$y = $image->getHeight();
|
||||||
$this->stateChanges("width", $x);
|
$this->stateChanges("width", $x);
|
||||||
$this->stateChanges("height", $y);
|
$this->stateChanges("height", $y);
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$x, $y];
|
return [$x, $y];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlbum(): ?Album
|
function getAlbum(): ?Album
|
||||||
{
|
{
|
||||||
|
@ -80,10 +216,14 @@ class Photo extends Media
|
||||||
$res->height = $this->getDimensions()[1];
|
$res->height = $this->getDimensions()[1];
|
||||||
$res->date = $res->created = $this->getPublicationTime()->timestamp();
|
$res->date = $res->created = $this->getPublicationTime()->timestamp();
|
||||||
|
|
||||||
$res->src =
|
$res->sizes = $this->getVkApiSizes();
|
||||||
$res->src_small = $res->src_big = $res->src_xbig = $res->src_xxbig =
|
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
|
||||||
$res->src_xxxbig = $res->photo_75 = $res->photo_130 = $res->photo_604 =
|
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
|
||||||
$res->photo_807 = $res->photo_1280 = $res->photo_2560 = $this->getURL();
|
$res->src_big = $res->photo_604 = $this->getURLBySizeId("normal");
|
||||||
|
$res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large");
|
||||||
|
$res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger");
|
||||||
|
$res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original");
|
||||||
|
$res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES");
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
"chillerlan/php-qrcode": "dev-main",
|
"chillerlan/php-qrcode": "dev-main",
|
||||||
"vearutop/php-obscene-censor-rus": "dev-master",
|
"vearutop/php-obscene-censor-rus": "dev-master",
|
||||||
"erusev/parsedown": "dev-master",
|
"erusev/parsedown": "dev-master",
|
||||||
"bhaktaraz/php-rss-generator": "dev-master"
|
"bhaktaraz/php-rss-generator": "dev-master",
|
||||||
|
"ext-simplexml": "*"
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev"
|
"minimum-stability": "dev"
|
||||||
}
|
}
|
||||||
|
|
13
data/photosizes.xml
Normal file
13
data/photosizes.xml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<PhotoSizes name="standard">
|
||||||
|
<Size id="miniscule" vkId="s" maxSize="75" requireProp="none" />
|
||||||
|
<Size id="tiny" vkId="m" maxSize="130" requireProp="none" />
|
||||||
|
<Size id="tinier" vkId="o" maxSize="130" requireProp="3:2" />
|
||||||
|
<Size id="xsmall" vkId="p" maxSize="200" requireProp="3:2" />
|
||||||
|
<Size id="small" vkId="q" maxSize="320" requireProp="3:2" />
|
||||||
|
<Size id="medium" vkId="r" maxSize="510" requireProp="3:2" />
|
||||||
|
<Size id="normal" vkId="x" maxSize="604" requireProp="none" />
|
||||||
|
<Size id="large" vkId="y" maxSize="807" requireProp="none" />
|
||||||
|
<Size id="larger" vkId="z" maxResolution="1080x1024" requireProp="none" />
|
||||||
|
<Size id="original" vkId="w" maxResolution="2560x2048" requireProp="none" />
|
||||||
|
</PhotoSizes>
|
|
@ -1,3 +1,3 @@
|
||||||
ALTER TABLE `photos` ROW_FORMAT=COMPRESSED;
|
ALTER TABLE `photos` ROW_FORMAT=COMPRESSED;
|
||||||
ALTER TABLE `photos` ADD COLUMN `sizes` VARBINARY(256) NULL DEFAULT NULL AFTER `hash`;
|
ALTER TABLE `photos` ADD COLUMN `sizes` VARBINARY(486) NULL DEFAULT NULL AFTER `hash`;
|
||||||
ALTER TABLE `photos` ADD COLUMN `width` SMALLINT UNSIGNED NULL DEFAULT NULL AFTER `sizes`, ADD COLUMN `height` SMALLINT UNSIGNED NULL DEFAULT NULL AFTER `sizes`;
|
ALTER TABLE `photos` ADD COLUMN `width` SMALLINT UNSIGNED NULL DEFAULT NULL AFTER `sizes`, ADD COLUMN `height` SMALLINT UNSIGNED NULL DEFAULT NULL AFTER `sizes`;
|
||||||
|
|
|
@ -17,6 +17,8 @@ openvk:
|
||||||
minLength: 3 # won't affect existing short urls or the ones set via admin panel
|
minLength: 3 # won't affect existing short urls or the ones set via admin panel
|
||||||
forbiddenNames:
|
forbiddenNames:
|
||||||
- "index.php"
|
- "index.php"
|
||||||
|
photos:
|
||||||
|
upgradeStructure: true
|
||||||
security:
|
security:
|
||||||
requireEmail: false
|
requireEmail: false
|
||||||
requirePhone: false
|
requirePhone: false
|
||||||
|
|
Loading…
Reference in a new issue