diff --git a/plugins/ImageThumbnail/Controller/ImageThumbnail.php b/plugins/ImageThumbnail/Controller/ImageThumbnail.php index 65b1147726..6239eed1e2 100644 --- a/plugins/ImageThumbnail/Controller/ImageThumbnail.php +++ b/plugins/ImageThumbnail/Controller/ImageThumbnail.php @@ -26,7 +26,6 @@ use App\Core\DB\DB; use App\Entity\AttachmentThumbnail; use App\Util\Common; use Component\Media\Media; -use Plugin\ImageThumbnail\ImageThumbnail as ThisPlugin; use Symfony\Component\HttpFoundation\Request; class ImageThumbnail extends Controller @@ -51,12 +50,13 @@ class ImageThumbnail extends Controller $height = Common::clamp($this->int('h') ?? $max_height, min: 0, max: $max_height); $crop = $this->bool('c') ?? false; - $filename = $attachment->getFilename(); - $filepath = ThisPlugin::getPath($attachment); - $thumbnail = AttachmentThumbnail::getOrCreate(attachment: $attachment, width: $width, height: $height, crop: $crop); - dd($thumbnail); + DB::persist($thumbnail); + DB::flush(); - return Media::sendFile(filepath: $filepath, mimetype: $attachment->getMimetype(), output_filename: $filename, disposition: 'inline'); + $filename = $thumbnail->getFilename(); + $path = $thumbnail->getPath(); + + return Media::sendFile(filepath: $path, mimetype: $attachment->getMimetype(), output_filename: $filename, disposition: 'inline'); } } diff --git a/plugins/ImageThumbnail/ImageThumbnail.php b/plugins/ImageThumbnail/ImageThumbnail.php index d6a8f4cde5..352dce86f5 100644 --- a/plugins/ImageThumbnail/ImageThumbnail.php +++ b/plugins/ImageThumbnail/ImageThumbnail.php @@ -24,8 +24,9 @@ use function App\Core\I18n\_m; use App\Core\Modules\Module; use App\Core\Router\RouteLoader; use App\Entity\Attachment; +use App\Entity\AttachmentThumbnail; use App\Util\Common; -use Intervention\Image\Image; +use Jcupitt\Vips; class ImageThumbnail extends Module { @@ -35,11 +36,6 @@ class ImageThumbnail extends Module return Event::next; } - public static function getPath(Attachment $attachment) - { - return Common::config('attachments', 'dir') . $attachment->getFilename(); - } - /** * Resizes an image. It will reencode the image in the * `self::prefferedType()` format. This only applies henceforward, @@ -51,32 +47,29 @@ class ImageThumbnail extends Module * * @throws Exception */ - public function onResizeImage(Attachment $attachment, string $outpath, int $width, int $height, bool $crop) + public function onResizeImage(Attachment $attachment, AttachmentThumbnail $thumbnail, int $width, int $height, bool $crop) { $old_limit = ini_set('memory_limit', Common::config('attachments', 'memory_limit')); - // try { - // $img = Image::make($this->filepath); - // } catch (Exception $e) { - // Log::error(__METHOD__ . ' encountered exception: ' . print_r($e, true)); - // // TRANS: Exception thrown when trying to resize an unknown file type. - // throw new Exception(_m('Unknown file type')); - // } + try { + // -1 means load all pages, 'sequential' access means decode pixels on demand + // $image = Vips\Image::newFromFile(self::getPath($attachment), ['n' => -1, 'access' => 'sequential']); + $image = Vips\Image::thumbnail($attachment->getPath(), $width, ['height' => $height]); + } catch (Exception $e) { + Log::error(__METHOD__ . ' encountered exception: ' . print_r($e, true)); + // TRANS: Exception thrown when trying to resize an unknown file type. + throw new Exception(_m('Unknown file type')); + } - // if (self::getPath($attachment) === $outpath) { - // @unlink($outpath); - // } + if ($attachment->getPath() === $thumbnail->getPath()) { + @unlink($thumbnail->getPath()); + } - // // Fit image to dimensions and optionally prevent upscaling - // if (!$crop) $img->fit($width, $height, function ($constraint) { $constraint->upsize();}); - // else $img->crop($width, $height); + $image->writeToFile($thumbnail->getPath()); + unset($image); - // $img->save($outpath, 100, 'webp'); - // $img->destroy(); + ini_set('memory_limit', $old_limit); // Restore the old memory limit - // ini_set('memory_limit', $old_limit); // Restore the old memory limit - - // return $outpath; return Event::next; } } diff --git a/plugins/ImageThumbnail/composer.json b/plugins/ImageThumbnail/composer.json index a67422f77a..f64071c66f 100644 --- a/plugins/ImageThumbnail/composer.json +++ b/plugins/ImageThumbnail/composer.json @@ -1,5 +1,5 @@ { "require": { - "intervention/image": "2.5.1" + "jcupitt/vips": "1.0.8" } } diff --git a/src/Entity/Attachment.php b/src/Entity/Attachment.php index e9c18172e6..fb449b305a 100644 --- a/src/Entity/Attachment.php +++ b/src/Entity/Attachment.php @@ -23,6 +23,7 @@ namespace App\Entity; use App\Core\DB\DB; use App\Core\Entity; +use App\Util\Common; use DateTimeInterface; /** @@ -227,6 +228,11 @@ class Attachment extends Entity } } + public function getPath() + { + return Common::config('attachments', 'dir') . $this->getFilename(); + } + public static function schemaDef(): array { return [ diff --git a/src/Entity/AttachmentThumbnail.php b/src/Entity/AttachmentThumbnail.php index 693571ca66..74e332644a 100644 --- a/src/Entity/AttachmentThumbnail.php +++ b/src/Entity/AttachmentThumbnail.php @@ -25,6 +25,7 @@ use App\Core\DB\DB; use App\Core\Entity; use App\Core\Event; use App\Core\Log; +use App\Util\Common; use App\Util\Exception\NotFoundException; use App\Util\Exception\ServerException; use Component\Media\Media; @@ -98,17 +99,34 @@ class AttachmentThumbnail extends Entity // }}} Autocode + private Attachment $attachment; + + public function setAttachment(Attachment $attachment) + { + $this->attachment = $attachment; + } + + public function getAttachment() + { + if (isset($this->attachment)) { + return $this->attachment; + } else { + return $this->attachment = DB::findOneBy('attachment', ['id' => $this->attachment_id]); + } + } + public static function getOrCreate(Attachment $attachment, ?int $width = null, ?int $height = null, ?bool $crop = null) { try { return DB::findOneBy('attachment_thumbnail', ['attachment_id' => $attachment->getId(), 'width' => $width, 'height' => $height]); } catch (NotFoundException $e) { - $outpath = $attachment->getFileHash() . '-' . $width . 'x' . $height; + $thumbnail = self::create(['attachment_id' => $attachment->getId(), 'width' => $width, 'height' => $height, 'attachment' => $attachment]); $event_map = ['image' => 'ResizeImage', 'video' => 'ResizeVideo']; $major_mime = Media::mimetypeMajor($attachment->getMimetype()); if (in_array($major_mime, array_keys($event_map))) { - Event::handle($event_map[$major_mime], [$attachment, $outpath, $width, $height, $crop]); + Event::handle($event_map[$major_mime], [$attachment, $thumbnail, $width, $height, $crop]); + return $thumbnail; } else { Log::debug($m = ('Cannot resize attachment with mimetype ' . $attachment->getMimetype())); throw new ServerException($m); @@ -116,6 +134,16 @@ class AttachmentThumbnail extends Entity } } + public function getFilename() + { + return $this->getAttachment()->getFileHash() . "-{$this->width}x{$this->height}.webp"; + } + + public function getPath() + { + return Common::config('thumbnail', 'dir') . $this->getFilename(); + } + /** * Delete a attachment thumbnail. This table doesn't own all the attachments, only itself */ diff --git a/src/Entity/Poll.php b/src/Entity/Poll.php index 36f0bdc5ad..d3da79fb5d 100644 --- a/src/Entity/Poll.php +++ b/src/Entity/Poll.php @@ -19,7 +19,7 @@ // }}} -namespace App\Entity; +namespace Plugin\Poll\Entity; use App\Core\DB\DB; use App\Core\Entity; diff --git a/src/Entity/PollResponse.php b/src/Entity/PollResponse.php index 7314964e99..02b9988ae9 100644 --- a/src/Entity/PollResponse.php +++ b/src/Entity/PollResponse.php @@ -19,7 +19,7 @@ // }}} -namespace App\Entity; +namespace Plugin\Poll\Entity; use App\Core\DB\DB; use App\Core\Entity;