fix(blurhash): Use preview API to generate the previews

This allows to benefit from all the checks done by the preview API.

This also use the newly introduced `cacheResult` argument to limit disk usage.

Signed-off-by: Louis Chemineau <louis@chmn.me>
This commit is contained in:
Louis Chemineau 2025-05-13 11:15:55 +02:00
parent d73b9bdcf7
commit 62c2525c19
No known key found for this signature in database
5 changed files with 26 additions and 83 deletions

View file

@ -1701,12 +1701,6 @@
<code><![CDATA[$image]]></code>
<code><![CDATA[$image]]></code>
</InvalidArgument>
<InvalidReturnStatement>
<code><![CDATA[($newImage !== false) ? $newImage : $image]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[GdImage|false]]></code>
</InvalidReturnType>
</file>
<file src="lib/private/Cache/CappedMemoryCache.php">
<MissingTemplateParam>
@ -2562,24 +2556,9 @@
</InvalidReturnStatement>
</file>
<file src="lib/private/Preview/Generator.php">
<InvalidArgument>
<code><![CDATA[$maxPreviewImage]]></code>
</InvalidArgument>
<LessSpecificReturnType>
<code><![CDATA[null|string]]></code>
</LessSpecificReturnType>
<MismatchingDocblockParamType>
<code><![CDATA[ISimpleFile]]></code>
</MismatchingDocblockParamType>
<UndefinedInterfaceMethod>
<code><![CDATA[height]]></code>
<code><![CDATA[height]]></code>
<code><![CDATA[preciseResizeCopy]]></code>
<code><![CDATA[resizeCopy]]></code>
<code><![CDATA[valid]]></code>
<code><![CDATA[width]]></code>
<code><![CDATA[width]]></code>
</UndefinedInterfaceMethod>
</file>
<file src="lib/private/Preview/ProviderV1Adapter.php">
<InvalidReturnStatement>

View file

@ -36,6 +36,7 @@ use OCP\Files\NotPermittedException;
use OCP\FilesMetadata\AMetadataEvent;
use OCP\FilesMetadata\Event\MetadataBackgroundEvent;
use OCP\FilesMetadata\Event\MetadataLiveEvent;
use OCP\IPreview;
use OCP\Lock\LockedException;
/**
@ -44,11 +45,14 @@ use OCP\Lock\LockedException;
* @template-implements IEventListener<AMetadataEvent>
*/
class GenerateBlurhashMetadata implements IEventListener {
private const RESIZE_BOXSIZE = 30;
private const COMPONENTS_X = 4;
private const COMPONENTS_Y = 3;
public function __construct(
private IPreview $preview,
) {
}
/**
* @throws NotPermittedException
* @throws GenericFileException
@ -81,7 +85,9 @@ class GenerateBlurhashMetadata implements IEventListener {
return;
}
$image = $this->resizedImageFromFile($file);
$preview = $this->preview->getPreview($file, 64, 64, cacheResult: false);
$image = @imagecreatefromstring($preview->getContent());
if (!$image) {
return;
}
@ -90,35 +96,6 @@ class GenerateBlurhashMetadata implements IEventListener {
->setEtag('blurhash', $currentEtag);
}
/**
* @param File $file
*
* @return GdImage|false
* @throws GenericFileException
* @throws NotPermittedException
* @throws LockedException
*/
private function resizedImageFromFile(File $file): GdImage|false {
$image = @imagecreatefromstring($file->getContent());
if ($image === false) {
return false;
}
$currX = imagesx($image);
$currY = imagesy($image);
if ($currX > $currY) {
$newX = self::RESIZE_BOXSIZE;
$newY = intval($currY * $newX / $currX);
} else {
$newY = self::RESIZE_BOXSIZE;
$newX = intval($currX * $newY / $currY);
}
$newImage = @imagescale($image, $newX, $newY);
return ($newImage !== false) ? $newImage : $image;
}
/**
* @param GdImage $image
*
@ -133,6 +110,7 @@ class GenerateBlurhashMetadata implements IEventListener {
$row = [];
for ($x = 0; $x < $width; ++$x) {
$index = imagecolorat($image, $x, $y);
/** @psalm-suppress InvalidArgument false positive, GdImage is ok since 8.0 */
$colors = imagecolorsforindex($image, $index);
$row[] = [$colors['red'], $colors['green'], $colors['blue']];
}

View file

@ -189,7 +189,7 @@ class Generator {
$maxPreviewImage = $this->helper->getImage($maxPreview);
}
$preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion);
$preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion, $cacheResult);
// New file, augment our array
$previewFiles[] = $preview;
}
@ -368,11 +368,10 @@ class Generator {
$path = $this->generatePath($preview->width(), $preview->height(), $crop, $max, $preview->dataMimeType(), $prefix);
try {
$file = $previewFolder->newFile($path);
if ($preview instanceof IStreamImage) {
$file->putContent($preview->resource());
return $previewFolder->newFile($path, $preview->resource());
} else {
$file->putContent($preview->data());
return $previewFolder->newFile($path, $preview->data());
}
} catch (NotPermittedException $e) {
throw new NotFoundException();
@ -558,14 +557,13 @@ class Generator {
$path = $this->generatePath($width, $height, $crop, false, $preview->dataMimeType(), $prefix);
try {
if ($cacheResult) {
$file = $previewFolder->newFile($path, $preview->data());
return $previewFolder->newFile($path, $preview->data());
} else {
return new InMemoryFile($path, $preview->data());
}
} catch (NotPermittedException $e) {
throw new NotFoundException();
}
return $file;
}

View file

@ -37,19 +37,19 @@ use OCP\Preview\BeforePreviewFetchedEvent;
use OCP\Preview\IProviderV2;
class GeneratorTest extends \Test\TestCase {
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
/** @var IConfig&\PHPUnit\Framework\MockObject\MockObject */
private $config;
/** @var IPreview|\PHPUnit\Framework\MockObject\MockObject */
/** @var IPreview&\PHPUnit\Framework\MockObject\MockObject */
private $previewManager;
/** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */
/** @var IAppData&\PHPUnit\Framework\MockObject\MockObject */
private $appData;
/** @var GeneratorHelper|\PHPUnit\Framework\MockObject\MockObject */
/** @var GeneratorHelper&\PHPUnit\Framework\MockObject\MockObject */
private $helper;
/** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
/** @var IEventDispatcher&\PHPUnit\Framework\MockObject\MockObject */
private $eventDispatcher;
/** @var Generator */
@ -207,18 +207,10 @@ class GeneratorTest extends \Test\TestCase {
$previewFolder->method('getDirectoryListing')
->willReturn([]);
$previewFolder->method('newFile')
->willReturnCallback(function ($filename) use ($maxPreview, $previewFile) {
if ($filename === '2048-2048-max.png') {
return $maxPreview;
} elseif ($filename === '256-256.png') {
return $previewFile;
}
$this->fail('Unexpected file');
});
$maxPreview->expects($this->once())
->method('putContent')
->with($this->equalTo('my data'));
->willReturnMap([
['2048-2048-max.png', 'my data', $maxPreview],
['256-256.png', 'my resized data', $previewFile],
]);
$previewFolder->method('getFile')
->with($this->equalTo('256-256.png'))
@ -229,10 +221,6 @@ class GeneratorTest extends \Test\TestCase {
->with($this->equalTo($maxPreview))
->willReturn($image);
$previewFile->expects($this->once())
->method('putContent')
->with('my resized data');
$this->eventDispatcher->expects($this->once())
->method('dispatchTyped')
->with(new BeforePreviewFetchedEvent($file, 100, 100, false, IPreview::MODE_FILL));

View file

@ -2247,11 +2247,11 @@
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"platform": {},
"platform-dev": {},
"platform-overrides": {
"php": "8.0"
},