Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not generate unnecessary max preview file #17028

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 25 additions & 94 deletions lib/private/Preview/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,16 @@

namespace OC\Preview;

use OC\Preview\GeneratorHelper;
use OCP\Files\File;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
use OCP\IImage;
use OCP\Image as OCPImage;
use OCP\IPreview;
use OCP\Preview\IProvider;
use OCP\Preview\IVersionedPreviewFile;
use OCP\Preview\IProviderV2;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;

Expand All @@ -49,8 +46,6 @@ class Generator {
private $config;
/** @var IAppData */
private $appData;
/** @var GeneratorHelper */
private $helper;
/** @var EventDispatcherInterface */
private $eventDispatcher;

Expand All @@ -71,8 +66,8 @@ public function __construct(
$this->config = $config;
$this->previewManager = $previewManager;
$this->appData = $appData;
$this->helper = $helper;
$this->eventDispatcher = $eventDispatcher;
//TODO remove GeneratorHelper $helper
}

/**
Expand Down Expand Up @@ -122,14 +117,18 @@ public function getPreview(File $file, $width = -1, $height = -1, $crop = false,
$previewVersion = $file->getPreviewVersion() . '-';
}

// Get the max preview and infer the max preview sizes from that
$maxPreview = $this->getMaxPreview($previewFolder, $file, $mimeType, $previewVersion);
if ($maxPreview->getSize() === 0) {
$maxPreview->delete();
throw new NotFoundException('Max preview size 0, invalid!');
}

list($maxWidth, $maxHeight) = $this->getPreviewSize($maxPreview, $previewVersion);
// Compute max width and height preserving aspect ratio
// TODO test this with images smaller than preview_max_x and/or preview_max_y
// TODO Does this code really belong to here... see lib/private/legacy/image.php::resize() for example
$image = new OCPImage();
$image->loadFromData($file->getContent());
$heightOrig = $image->height();
$widthOrig = $image->width();
$ratio = $widthOrig / $heightOrig;
$maxWidthConfig = (int)$this->config->getSystemValue('preview_max_x', 4096);
$maxHeightConfig = (int)$this->config->getSystemValue('preview_max_y', 4096);
$maxWidth = min($maxWidthConfig, $ratio * $maxHeightConfig);
$maxHeight = min($maxHeightConfig, $maxWidthConfig / $ratio);

// If both width and heigth are -1 we just want the max preview
if ($width === -1 && $height === -1) {
Expand All @@ -141,16 +140,19 @@ public function getPreview(File $file, $width = -1, $height = -1, $crop = false,
list($width, $height) = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight);

// No need to generate a preview that is just the max preview
// TODO think about this use case...
/*
if ($width === $maxWidth && $height === $maxHeight) {
return $maxPreview;
}
}*/


// Try to get a cached preview. Else generate (and store) one
try {
try {
$preview = $this->getCachedPreview($previewFolder, $width, $height, $crop, $maxPreview->getMimeType(), $previewVersion);
$preview = $this->getCachedPreview($previewFolder, $width, $height, $crop, $mimeType, $previewVersion);
} catch (NotFoundException $e) {
$preview = $this->generatePreview($previewFolder, $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion);
$preview = $this->generatePreview($previewFolder, $image, $width, $height, $crop, $previewVersion);
}
} catch (\InvalidArgumentException $e) {
throw new NotFoundException();
Expand All @@ -164,72 +166,6 @@ public function getPreview(File $file, $width = -1, $height = -1, $crop = false,
return $preview;
}

/**
* @param ISimpleFolder $previewFolder
* @param File $file
* @param string $mimeType
* @param string $prefix
* @return ISimpleFile
* @throws NotFoundException
*/
private function getMaxPreview(ISimpleFolder $previewFolder, File $file, $mimeType, $prefix) {
$nodes = $previewFolder->getDirectoryListing();

foreach ($nodes as $node) {
$name = $node->getName();
if (($prefix === '' || strpos($name, $prefix) === 0) && strpos($name, 'max')) {
return $node;
}
}

$previewProviders = $this->previewManager->getProviders();
foreach ($previewProviders as $supportedMimeType => $providers) {
if (!preg_match($supportedMimeType, $mimeType)) {
continue;
}

foreach ($providers as $providerClosure) {
$provider = $this->helper->getProvider($providerClosure);
if (!($provider instanceof IProviderV2)) {
continue;
}

if (!$provider->isAvailable($file)) {
continue;
}

$maxWidth = (int)$this->config->getSystemValue('preview_max_x', 4096);
$maxHeight = (int)$this->config->getSystemValue('preview_max_y', 4096);

$preview = $this->helper->getThumbnail($provider, $file, $maxWidth, $maxHeight);

if (!($preview instanceof IImage)) {
continue;
}

// Try to get the extention.
try {
$ext = $this->getExtention($preview->dataMimeType());
} catch (\InvalidArgumentException $e) {
// Just continue to the next iteration if this preview doesn't have a valid mimetype
continue;
}

$path = $prefix . (string)$preview->width() . '-' . (string)$preview->height() . '-max.' . $ext;
try {
$file = $previewFolder->newFile($path);
$file->putContent($preview->data());
} catch (NotPermittedException $e) {
throw new NotFoundException();
}

return $file;
}
}

throw new NotFoundException();
}

/**
* @param ISimpleFile $file
* @param string $prefix
Expand Down Expand Up @@ -354,31 +290,26 @@ private function calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHei
* @param int $width
* @param int $height
* @param bool $crop
* @param int $maxWidth
* @param int $maxHeight
* @param string $prefix
* @return ISimpleFile
* @throws NotFoundException
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
*/
private function generatePreview(ISimpleFolder $previewFolder, ISimpleFile $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $prefix) {
$preview = $this->helper->getImage($maxPreview);

private function generatePreview(ISimpleFolder $previewFolder, OCPImage $preview, $width, $height, $crop, $prefix) {
if (!$preview->valid()) {
throw new \InvalidArgumentException('Failed to generate preview, failed to load image');
}

if ($crop) {
if ($height !== $preview->height() && $width !== $preview->width()) {
//Resize
$widthR = $preview->width() / $width;
$heightR = $preview->height() / $height;
$ratioOrig = $preview->width() / $preview->height();

if ($widthR > $heightR) {
if ($ratioOrig > 1) {
$scaleH = $height;
$scaleW = $maxWidth / $heightR;
$scaleW = $height * $ratioOrig;
} else {
$scaleH = $maxHeight / $widthR;
$scaleH = $width / $ratioOrig;
$scaleW = $width;
}
$preview->preciseResize((int)round($scaleW), (int)round($scaleH));
Expand Down