migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
class BMP extends Image {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/bmp/';
}
}
@@ -0,0 +1,173 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OC\Preview\Storage\Root;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\IMimeTypeLoader;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IDBConnection;
class BackgroundCleanupJob extends TimedJob {
/** @var IDBConnection */
private $connection;
/** @var Root */
private $previewFolder;
/** @var bool */
private $isCLI;
/** @var IMimeTypeLoader */
private $mimeTypeLoader;
public function __construct(ITimeFactory $timeFactory,
IDBConnection $connection,
Root $previewFolder,
IMimeTypeLoader $mimeTypeLoader,
bool $isCLI) {
parent::__construct($timeFactory);
// Run at most once an hour
$this->setInterval(3600);
$this->connection = $connection;
$this->previewFolder = $previewFolder;
$this->isCLI = $isCLI;
$this->mimeTypeLoader = $mimeTypeLoader;
}
public function run($argument) {
foreach ($this->getDeletedFiles() as $fileId) {
try {
$preview = $this->previewFolder->getFolder((string)$fileId);
$preview->delete();
} catch (NotFoundException $e) {
// continue
} catch (NotPermittedException $e) {
// continue
}
}
}
private function getDeletedFiles(): \Iterator {
yield from $this->getOldPreviewLocations();
yield from $this->getNewPreviewLocations();
}
private function getOldPreviewLocations(): \Iterator {
$qb = $this->connection->getQueryBuilder();
$qb->select('a.name')
->from('filecache', 'a')
->leftJoin('a', 'filecache', 'b', $qb->expr()->eq(
$qb->expr()->castColumn('a.name', IQueryBuilder::PARAM_INT), 'b.fileid'
))
->where(
$qb->expr()->isNull('b.fileid')
)->andWhere(
$qb->expr()->eq('a.parent', $qb->createNamedParameter($this->previewFolder->getId()))
)->andWhere(
$qb->expr()->like('a.name', $qb->createNamedParameter('__%'))
);
if (!$this->isCLI) {
$qb->setMaxResults(10);
}
$cursor = $qb->execute();
while ($row = $cursor->fetch()) {
yield $row['name'];
}
$cursor->closeCursor();
}
private function getNewPreviewLocations(): \Iterator {
$qb = $this->connection->getQueryBuilder();
$qb->select('path', 'mimetype')
->from('filecache')
->where($qb->expr()->eq('fileid', $qb->createNamedParameter($this->previewFolder->getId())));
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === null) {
return [];
}
/*
* This lovely like is the result of the way the new previews are stored
* We take the md5 of the name (fileid) and split the first 7 chars. That way
* there are not a gazillion files in the root of the preview appdata.
*/
$like = $this->connection->escapeLikeParameter($data['path']) . '/_/_/_/_/_/_/_/%';
/*
* Deleting a file will not delete related previews right away.
*
* A delete request is usually an HTTP request.
* The preview deleting is done by a background job to avoid timeouts.
*
* Previews for a file are stored within a folder in appdata_/preview using the fileid as folder name.
* Preview folders in oc_filecache are identified by a.storage, a.path (cf. $like) and a.mimetype.
*
* To find preview folders to delete, we query oc_filecache for a preview folder in app data, matching the preview folder structure
* and use the name to left join oc_filecache on a.name = b.fileid. A left join returns all rows from the left table (a),
* even if there are no matches in the right table (b).
*
* If the related file is deleted, b.fileid will be null and the preview folder can be deleted.
*/
$qb = $this->connection->getQueryBuilder();
$qb->select('a.name')
->from('filecache', 'a')
->leftJoin('a', 'filecache', 'b', $qb->expr()->eq(
$qb->expr()->castColumn('a.name', IQueryBuilder::PARAM_INT), 'b.fileid'
))
->where(
$qb->expr()->andX(
$qb->expr()->eq('a.storage', $qb->createNamedParameter($this->previewFolder->getStorageId())),
$qb->expr()->isNull('b.fileid'),
$qb->expr()->like('a.path', $qb->createNamedParameter($like)),
$qb->expr()->eq('a.mimetype', $qb->createNamedParameter($this->mimeTypeLoader->getId('httpd/unix-directory')))
)
);
if (!$this->isCLI) {
$qb->setMaxResults(10);
}
$cursor = $qb->execute();
while ($row = $cursor->fetch()) {
yield $row['name'];
}
$cursor->closeCursor();
}
}
+126
View File
@@ -0,0 +1,126 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use Imagick;
use OCP\Files\File;
use OCP\IImage;
use Psr\Log\LoggerInterface;
/**
* Creates a PNG preview using ImageMagick via the PECL extension
*
* @package OC\Preview
*/
abstract class Bitmap extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
$tmpPath = $this->getLocalFile($file);
if ($tmpPath === false) {
\OC::$server->get(LoggerInterface::class)->error(
'Failed to get thumbnail for: ' . $file->getPath(),
['app' => 'core']
);
return null;
}
// Creates \Imagick object from bitmap or vector file
try {
$bp = $this->getResizedPreview($tmpPath, $maxX, $maxY);
} catch (\Exception $e) {
\OC::$server->get(LoggerInterface::class)->info(
'File: ' . $file->getPath() . ' Imagick says:',
[
'exception' => $e,
'app' => 'core',
]
);
return null;
}
$this->cleanTmpFiles();
//new bitmap image object
$image = new \OCP\Image();
$image->loadFromData((string) $bp);
//check if image object is valid
return $image->valid() ? $image : null;
}
/**
* Returns a preview of maxX times maxY dimensions in PNG format
*
* * The default resolution is already 72dpi, no need to change it for a bitmap output
* * It's possible to have proper colour conversion using profileimage().
* ICC profiles are here: http://www.color.org/srgbprofiles.xalter
* * It's possible to Gamma-correct an image via gammaImage()
*
* @param string $tmpPath the location of the file to convert
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function getResizedPreview($tmpPath, $maxX, $maxY) {
$bp = new Imagick();
// Layer 0 contains either the bitmap or a flat representation of all vector layers
$bp->readImage($tmpPath . '[0]');
$bp = $this->resize($bp, $maxX, $maxY);
$bp->setImageFormat('png');
return $bp;
}
/**
* Returns a resized \Imagick object
*
* If you want to know more on the various methods available to resize an
* image, check out this link : @link https://stackoverflow.com/questions/8517304/what-the-difference-of-sample-resample-scale-resize-adaptive-resize-thumbnail-im
*
* @param \Imagick $bp
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function resize($bp, $maxX, $maxY) {
[$previewWidth, $previewHeight] = array_values($bp->getImageGeometry());
// We only need to resize a preview which doesn't fit in the maximum dimensions
if ($previewWidth > $maxX || $previewHeight > $maxY) {
// TODO: LANCZOS is the default filter, CATROM could bring similar results faster
$bp->resizeImage($maxX, $maxY, imagick::FILTER_LANCZOS, 1, true);
}
return $bp;
}
}
@@ -0,0 +1,59 @@
<?php
/**
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OC\Archive\ZIP;
use OCP\Files\File;
use OCP\IImage;
/**
* Extracts a preview from files that embed them in an ZIP archive
*/
abstract class Bundled extends ProviderV2 {
protected function extractThumbnail(File $file, string $path): ?IImage {
if ($file->getSize() === 0) {
return null;
}
$sourceTmp = \OC::$server->getTempManager()->getTemporaryFile();
$targetTmp = \OC::$server->getTempManager()->getTemporaryFile();
$this->tmpFiles[] = $sourceTmp;
$this->tmpFiles[] = $targetTmp;
try {
$content = $file->fopen('r');
file_put_contents($sourceTmp, $content);
$zip = new ZIP($sourceTmp);
$zip->extractFile($path, $targetTmp);
$image = new \OCP\Image();
$image->loadFromFile($targetTmp);
$image->fixOrientation();
return $image;
} catch (\Throwable $e) {
return null;
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* @copyright 2023 Daniel Kesselberg <mail@danielkesselberg.de>
*
* @author Daniel Kesselberg <mail@danielkesselberg.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
class EMF extends Office {
public function getMimeType(): string {
return '/image\/emf/';
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
// .otf, .ttf and .pfb
class Font extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/(?:font-sfnt|x-font$)/';
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
class GIF extends Image {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/gif/';
}
}
@@ -0,0 +1,632 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Elijah Martin-Merrill <elijah@nyp-itsours.com>
* @author J0WI <J0WI@users.noreply.github.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Scott Dutton <scott@exussum.co.uk>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
use OCP\Files\IAppData;
use OCP\Files\InvalidPathException;
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\IPreview;
use OCP\IStreamImage;
use OCP\Preview\BeforePreviewFetchedEvent;
use OCP\Preview\IProviderV2;
use OCP\Preview\IVersionedPreviewFile;
class Generator {
public const SEMAPHORE_ID_ALL = 0x0a11;
public const SEMAPHORE_ID_NEW = 0x07ea;
/** @var IPreview */
private $previewManager;
/** @var IConfig */
private $config;
/** @var IAppData */
private $appData;
/** @var GeneratorHelper */
private $helper;
/** @var IEventDispatcher */
private $eventDispatcher;
public function __construct(
IConfig $config,
IPreview $previewManager,
IAppData $appData,
GeneratorHelper $helper,
IEventDispatcher $eventDispatcher
) {
$this->config = $config;
$this->previewManager = $previewManager;
$this->appData = $appData;
$this->helper = $helper;
$this->eventDispatcher = $eventDispatcher;
}
/**
* Returns a preview of a file
*
* The cache is searched first and if nothing usable was found then a preview is
* generated by one of the providers
*
* @param File $file
* @param int $width
* @param int $height
* @param bool $crop
* @param string $mode
* @param string|null $mimeType
* @return ISimpleFile
* @throws NotFoundException
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
*/
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
$specification = [
'width' => $width,
'height' => $height,
'crop' => $crop,
'mode' => $mode,
];
$this->eventDispatcher->dispatchTyped(new BeforePreviewFetchedEvent(
$file,
$width,
$height,
$crop,
$mode,
));
// since we only ask for one preview, and the generate method return the last one it created, it returns the one we want
return $this->generatePreviews($file, [$specification], $mimeType);
}
/**
* Generates previews of a file
*
* @param File $file
* @param non-empty-array $specifications
* @param string $mimeType
* @return ISimpleFile the last preview that was generated
* @throws NotFoundException
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
*/
public function generatePreviews(File $file, array $specifications, $mimeType = null) {
//Make sure that we can read the file
if (!$file->isReadable()) {
throw new NotFoundException('Cannot read file');
}
if ($mimeType === null) {
$mimeType = $file->getMimeType();
}
$previewFolder = $this->getPreviewFolder($file);
// List every existing preview first instead of trying to find them one by one
$previewFiles = $previewFolder->getDirectoryListing();
$previewVersion = '';
if ($file instanceof IVersionedPreviewFile) {
$previewVersion = $file->getPreviewVersion() . '-';
}
// Get the max preview and infer the max preview sizes from that
$maxPreview = $this->getMaxPreview($previewFolder, $previewFiles, $file, $mimeType, $previewVersion);
$maxPreviewImage = null; // only load the image when we need it
if ($maxPreview->getSize() === 0) {
$maxPreview->delete();
throw new NotFoundException('Max preview size 0, invalid!');
}
[$maxWidth, $maxHeight] = $this->getPreviewSize($maxPreview, $previewVersion);
if ($maxWidth <= 0 || $maxHeight <= 0) {
throw new NotFoundException('The maximum preview sizes are zero or less pixels');
}
$preview = null;
foreach ($specifications as $specification) {
$width = $specification['width'] ?? -1;
$height = $specification['height'] ?? -1;
$crop = $specification['crop'] ?? false;
$mode = $specification['mode'] ?? IPreview::MODE_FILL;
// If both width and height are -1 we just want the max preview
if ($width === -1 && $height === -1) {
$width = $maxWidth;
$height = $maxHeight;
}
// Calculate the preview size
[$width, $height] = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight);
// No need to generate a preview that is just the max preview
if ($width === $maxWidth && $height === $maxHeight) {
// ensure correct return value if this was the last one
$preview = $maxPreview;
continue;
}
// Try to get a cached preview. Else generate (and store) one
try {
try {
$preview = $this->getCachedPreview($previewFiles, $width, $height, $crop, $maxPreview->getMimeType(), $previewVersion);
} catch (NotFoundException $e) {
if (!$this->previewManager->isMimeSupported($mimeType)) {
throw new NotFoundException();
}
if ($maxPreviewImage === null) {
$maxPreviewImage = $this->helper->getImage($maxPreview);
}
$preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion);
// New file, augment our array
$previewFiles[] = $preview;
}
} catch (\InvalidArgumentException $e) {
throw new NotFoundException("", 0, $e);
}
if ($preview->getSize() === 0) {
$preview->delete();
throw new NotFoundException('Cached preview size 0, invalid!');
}
}
assert($preview !== null);
// Free memory being used by the embedded image resource. Without this the image is kept in memory indefinitely.
// Garbage Collection does NOT free this memory. We have to do it ourselves.
if ($maxPreviewImage instanceof \OCP\Image) {
$maxPreviewImage->destroy();
}
return $preview;
}
/**
* Acquire a semaphore of the specified id and concurrency, blocking if necessary.
* Return an identifier of the semaphore on success, which can be used to release it via
* {@see Generator::unguardWithSemaphore()}.
*
* @param int $semId
* @param int $concurrency
* @return false|\SysvSemaphore the semaphore on success or false on failure
*/
public static function guardWithSemaphore(int $semId, int $concurrency) {
if (!extension_loaded('sysvsem')) {
return false;
}
$sem = sem_get($semId, $concurrency);
if ($sem === false) {
return false;
}
if (!sem_acquire($sem)) {
return false;
}
return $sem;
}
/**
* Releases the semaphore acquired from {@see Generator::guardWithSemaphore()}.
*
* @param false|\SysvSemaphore $semId the semaphore identifier returned by guardWithSemaphore
* @return bool
*/
public static function unguardWithSemaphore(false|\SysvSemaphore $semId): bool {
if ($semId === false || !($semId instanceof \SysvSemaphore)) {
return false;
}
return sem_release($semId);
}
/**
* Get the number of concurrent threads supported by the host.
*
* @return int number of concurrent threads, or 0 if it cannot be determined
*/
public static function getHardwareConcurrency(): int {
static $width;
if (!isset($width)) {
if (function_exists('ini_get')) {
$openBasedir = ini_get('open_basedir');
if (empty($openBasedir) || strpos($openBasedir, '/proc/cpuinfo') !== false) {
$width = is_readable('/proc/cpuinfo') ? substr_count(file_get_contents('/proc/cpuinfo'), 'processor') : 0;
} else {
$width = 0;
}
} else {
$width = 0;
}
}
return $width;
}
/**
* Get number of concurrent preview generations from system config
*
* Two config entries, `preview_concurrency_new` and `preview_concurrency_all`,
* are available. If not set, the default values are determined with the hardware concurrency
* of the host. In case the hardware concurrency cannot be determined, or the user sets an
* invalid value, fallback values are:
* For new images whose previews do not exist and need to be generated, 4;
* For all preview generation requests, 8.
* Value of `preview_concurrency_all` should be greater than or equal to that of
* `preview_concurrency_new`, otherwise, the latter is returned.
*
* @param string $type either `preview_concurrency_new` or `preview_concurrency_all`
* @return int number of concurrent preview generations, or -1 if $type is invalid
*/
public function getNumConcurrentPreviews(string $type): int {
static $cached = array();
if (array_key_exists($type, $cached)) {
return $cached[$type];
}
$hardwareConcurrency = self::getHardwareConcurrency();
switch ($type) {
case "preview_concurrency_all":
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency * 2 : 8;
$concurrency_all = $this->config->getSystemValueInt($type, $fallback);
$concurrency_new = $this->getNumConcurrentPreviews("preview_concurrency_new");
$cached[$type] = max($concurrency_all, $concurrency_new);
break;
case "preview_concurrency_new":
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency : 4;
$cached[$type] = $this->config->getSystemValueInt($type, $fallback);
break;
default:
return -1;
}
return $cached[$type];
}
/**
* @param ISimpleFolder $previewFolder
* @param ISimpleFile[] $previewFiles
* @param File $file
* @param string $mimeType
* @param string $prefix
* @return ISimpleFile
* @throws NotFoundException
*/
private function getMaxPreview(ISimpleFolder $previewFolder, array $previewFiles, File $file, $mimeType, $prefix) {
// We don't know the max preview size, so we can't use getCachedPreview.
// It might have been generated with a higher resolution than the current value.
foreach ($previewFiles as $node) {
$name = $node->getName();
if (($prefix === '' || str_starts_with($name, $prefix)) && strpos($name, 'max')) {
return $node;
}
}
$maxWidth = $this->config->getSystemValueInt('preview_max_x', 4096);
$maxHeight = $this->config->getSystemValueInt('preview_max_y', 4096);
return $this->generateProviderPreview($previewFolder, $file, $maxWidth, $maxHeight, false, true, $mimeType, $prefix);
}
private function generateProviderPreview(ISimpleFolder $previewFolder, File $file, int $width, int $height, bool $crop, bool $max, string $mimeType, string $prefix) {
$previewProviders = $this->previewManager->getProviders();
foreach ($previewProviders as $supportedMimeType => $providers) {
// Filter out providers that does not support this mime
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;
}
$previewConcurrency = $this->getNumConcurrentPreviews('preview_concurrency_new');
$sem = self::guardWithSemaphore(self::SEMAPHORE_ID_NEW, $previewConcurrency);
try {
$preview = $this->helper->getThumbnail($provider, $file, $width, $height);
} finally {
self::unguardWithSemaphore($sem);
}
if (!($preview instanceof IImage)) {
continue;
}
$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());
} else {
$file->putContent($preview->data());
}
} catch (NotPermittedException $e) {
throw new NotFoundException();
}
return $file;
}
}
throw new NotFoundException('No provider successfully handled the preview generation');
}
/**
* @param ISimpleFile $file
* @param string $prefix
* @return int[]
*/
private function getPreviewSize(ISimpleFile $file, string $prefix = '') {
$size = explode('-', substr($file->getName(), strlen($prefix)));
return [(int)$size[0], (int)$size[1]];
}
/**
* @param int $width
* @param int $height
* @param bool $crop
* @param bool $max
* @param string $mimeType
* @param string $prefix
* @return string
*/
private function generatePath($width, $height, $crop, $max, $mimeType, $prefix) {
$path = $prefix . (string)$width . '-' . (string)$height;
if ($crop) {
$path .= '-crop';
}
if ($max) {
$path .= '-max';
}
$ext = $this->getExtension($mimeType);
$path .= '.' . $ext;
return $path;
}
/**
* @param int $width
* @param int $height
* @param bool $crop
* @param string $mode
* @param int $maxWidth
* @param int $maxHeight
* @return int[]
*/
private function calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight) {
/*
* If we are not cropping we have to make sure the requested image
* respects the aspect ratio of the original.
*/
if (!$crop) {
$ratio = $maxHeight / $maxWidth;
if ($width === -1) {
$width = $height / $ratio;
}
if ($height === -1) {
$height = $width * $ratio;
}
$ratioH = $height / $maxHeight;
$ratioW = $width / $maxWidth;
/*
* Fill means that the $height and $width are the max
* Cover means min.
*/
if ($mode === IPreview::MODE_FILL) {
if ($ratioH > $ratioW) {
$height = $width * $ratio;
} else {
$width = $height / $ratio;
}
} elseif ($mode === IPreview::MODE_COVER) {
if ($ratioH > $ratioW) {
$width = $height / $ratio;
} else {
$height = $width * $ratio;
}
}
}
if ($height !== $maxHeight && $width !== $maxWidth) {
/*
* Scale to the nearest power of four
*/
$pow4height = 4 ** ceil(log($height) / log(4));
$pow4width = 4 ** ceil(log($width) / log(4));
// Minimum size is 64
$pow4height = max($pow4height, 64);
$pow4width = max($pow4width, 64);
$ratioH = $height / $pow4height;
$ratioW = $width / $pow4width;
if ($ratioH < $ratioW) {
$width = $pow4width;
$height /= $ratioW;
} else {
$height = $pow4height;
$width /= $ratioH;
}
}
/*
* Make sure the requested height and width fall within the max
* of the preview.
*/
if ($height > $maxHeight) {
$ratio = $height / $maxHeight;
$height = $maxHeight;
$width /= $ratio;
}
if ($width > $maxWidth) {
$ratio = $width / $maxWidth;
$width = $maxWidth;
$height /= $ratio;
}
return [(int)round($width), (int)round($height)];
}
/**
* @param ISimpleFolder $previewFolder
* @param ISimpleFile $maxPreview
* @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, IImage $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $prefix) {
$preview = $maxPreview;
if (!$preview->valid()) {
throw new \InvalidArgumentException('Failed to generate preview, failed to load image');
}
$previewConcurrency = $this->getNumConcurrentPreviews('preview_concurrency_new');
$sem = self::guardWithSemaphore(self::SEMAPHORE_ID_NEW, $previewConcurrency);
try {
if ($crop) {
if ($height !== $preview->height() && $width !== $preview->width()) {
//Resize
$widthR = $preview->width() / $width;
$heightR = $preview->height() / $height;
if ($widthR > $heightR) {
$scaleH = $height;
$scaleW = $maxWidth / $heightR;
} else {
$scaleH = $maxHeight / $widthR;
$scaleW = $width;
}
$preview = $preview->preciseResizeCopy((int)round($scaleW), (int)round($scaleH));
}
$cropX = (int)floor(abs($width - $preview->width()) * 0.5);
$cropY = (int)floor(abs($height - $preview->height()) * 0.5);
$preview = $preview->cropCopy($cropX, $cropY, $width, $height);
} else {
$preview = $maxPreview->resizeCopy(max($width, $height));
}
} finally {
self::unguardWithSemaphore($sem);
}
$path = $this->generatePath($width, $height, $crop, false, $preview->dataMimeType(), $prefix);
try {
$file = $previewFolder->newFile($path);
$file->putContent($preview->data());
} catch (NotPermittedException $e) {
throw new NotFoundException();
}
return $file;
}
/**
* @param ISimpleFile[] $files Array of FileInfo, as the result of getDirectoryListing()
* @param int $width
* @param int $height
* @param bool $crop
* @param string $mimeType
* @param string $prefix
* @return ISimpleFile
*
* @throws NotFoundException
*/
private function getCachedPreview($files, $width, $height, $crop, $mimeType, $prefix) {
$path = $this->generatePath($width, $height, $crop, false, $mimeType, $prefix);
foreach ($files as $file) {
if ($file->getName() === $path) {
return $file;
}
}
throw new NotFoundException();
}
/**
* Get the specific preview folder for this file
*
* @param File $file
* @return ISimpleFolder
*
* @throws InvalidPathException
* @throws NotFoundException
* @throws NotPermittedException
*/
private function getPreviewFolder(File $file) {
// Obtain file id outside of try catch block to prevent the creation of an existing folder
$fileId = (string)$file->getId();
try {
$folder = $this->appData->getFolder($fileId);
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder($fileId);
}
return $folder;
}
/**
* @param string $mimeType
* @return null|string
* @throws \InvalidArgumentException
*/
private function getExtension($mimeType) {
switch ($mimeType) {
case 'image/png':
return 'png';
case 'image/jpeg':
return 'jpg';
case 'image/webp':
return 'webp';
case 'image/gif':
return 'gif';
default:
throw new \InvalidArgumentException('Not a valid mimetype: "' . $mimeType . '"');
}
}
}
@@ -0,0 +1,88 @@
<?php
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IConfig;
use OCP\IImage;
use OCP\Image as OCPImage;
use OCP\Preview\IProvider;
use OCP\Preview\IProviderV2;
/**
* Very small wrapper class to make the generator fully unit testable
*/
class GeneratorHelper {
/** @var IRootFolder */
private $rootFolder;
/** @var IConfig */
private $config;
public function __construct(IRootFolder $rootFolder, IConfig $config) {
$this->rootFolder = $rootFolder;
$this->config = $config;
}
/**
* @param IProviderV2 $provider
* @param File $file
* @param int $maxWidth
* @param int $maxHeight
*
* @return bool|IImage
*/
public function getThumbnail(IProviderV2 $provider, File $file, $maxWidth, $maxHeight, bool $crop = false) {
if ($provider instanceof Imaginary) {
return $provider->getCroppedThumbnail($file, $maxWidth, $maxHeight, $crop) ?? false;
}
return $provider->getThumbnail($file, $maxWidth, $maxHeight) ?? false;
}
/**
* @param ISimpleFile $maxPreview
* @return IImage
*/
public function getImage(ISimpleFile $maxPreview) {
$image = new OCPImage();
$image->loadFromData($maxPreview->getContent());
return $image;
}
/**
* @param callable $providerClosure
* @return IProviderV2
*/
public function getProvider($providerClosure) {
$provider = $providerClosure();
if ($provider instanceof IProvider) {
$provider = new ProviderV1Adapter($provider);
}
return $provider;
}
}
+161
View File
@@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018, ownCloud GmbH
* @copyright Copyright (c) 2018, Sebastian Steinmetz (me@sebastiansteinmetz.ch)
*
* @author J0WI <J0WI@users.noreply.github.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Sebastian Steinmetz <462714+steiny2k@users.noreply.github.com>
* @author Sebastian Steinmetz <me@sebastiansteinmetz.ch>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
use Psr\Log\LoggerInterface;
/**
* Creates a JPG preview using ImageMagick via the PECL extension
*
* @package OC\Preview
*/
class HEIC extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/hei(f|c)/';
}
/**
* {@inheritDoc}
*/
public function isAvailable(FileInfo $file): bool {
return in_array('HEIC', \Imagick::queryFormats("HEI*"));
}
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
if (!$this->isAvailable($file)) {
return null;
}
$tmpPath = $this->getLocalFile($file);
if ($tmpPath === false) {
\OC::$server->get(LoggerInterface::class)->error(
'Failed to get thumbnail for: ' . $file->getPath(),
['app' => 'core']
);
return null;
}
// Creates \Imagick object from the heic file
try {
$bp = $this->getResizedPreview($tmpPath, $maxX, $maxY);
$bp->setFormat('jpg');
} catch (\Exception $e) {
\OC::$server->get(LoggerInterface::class)->error(
'File: ' . $file->getPath() . ' Imagick says:',
[
'exception' => $e,
'app' => 'core',
]
);
return null;
}
$this->cleanTmpFiles();
//new bitmap image object
$image = new \OCP\Image();
$image->loadFromData((string) $bp);
//check if image object is valid
return $image->valid() ? $image : null;
}
/**
* Returns a preview of maxX times maxY dimensions in JPG format
*
* * The default resolution is already 72dpi, no need to change it for a bitmap output
* * It's possible to have proper colour conversion using profileimage().
* ICC profiles are here: http://www.color.org/srgbprofiles.xalter
* * It's possible to Gamma-correct an image via gammaImage()
*
* @param string $tmpPath the location of the file to convert
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function getResizedPreview($tmpPath, $maxX, $maxY) {
$bp = new \Imagick();
// Layer 0 contains either the bitmap or a flat representation of all vector layers
$bp->readImage($tmpPath . '[0]');
// Fix orientation from EXIF
$bp->autoOrient();
$bp->setImageFormat('jpg');
$bp = $this->resize($bp, $maxX, $maxY);
return $bp;
}
/**
* Returns a resized \Imagick object
*
* If you want to know more on the various methods available to resize an
* image, check out this link : @link https://stackoverflow.com/questions/8517304/what-the-difference-of-sample-resample-scale-resize-adaptive-resize-thumbnail-im
*
* @param \Imagick $bp
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function resize($bp, $maxX, $maxY) {
[$previewWidth, $previewHeight] = array_values($bp->getImageGeometry());
// We only need to resize a preview which doesn't fit in the maximum dimensions
if ($previewWidth > $maxX || $previewHeight > $maxY) {
// If we want a small image (thumbnail) let's be most space- and time-efficient
if ($maxX <= 500 && $maxY <= 500) {
$bp->thumbnailImage($maxY, $maxX, true);
$bp->stripImage();
} else {
// A bigger image calls for some better resizing algorithm
// According to http://www.imagemagick.org/Usage/filter/#lanczos
// the catrom filter is almost identical to Lanczos2, but according
// to https://www.php.net/manual/en/imagick.resizeimage.php it is
// significantly faster
$bp->resizeImage($maxX, $maxY, \Imagick::FILTER_CATROM, 1, true);
}
}
return $bp;
}
}
@@ -0,0 +1,40 @@
<?php
namespace OC\Preview;
use OCP\ICache;
use OCP\ICacheFactory;
class IMagickSupport {
private ICache $cache;
private ?\Imagick $imagick;
public function __construct(ICacheFactory $cacheFactory) {
$this->cache = $cacheFactory->createLocal('imagick');
if (extension_loaded('imagick')) {
$this->imagick = new \Imagick();
} else {
$this->imagick = null;
}
}
public function hasExtension(): bool {
return !is_null($this->imagick);
}
public function supportsFormat(string $format): bool {
if (is_null($this->imagick)) {
return false;
}
$cached = $this->cache->get($format);
if (!is_null($cached)) {
return $cached;
}
$formatSupported = count($this->imagick->queryFormats($format)) === 1;
$this->cache->set($format, $cached);
return $formatSupported;
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.ai
class Illustrator extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/illustrator/';
}
}
@@ -0,0 +1,63 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author J0WI <J0WI@users.noreply.github.com>
* @author Joas Schilling <coding@schilljs.com>
* @author josh4trunks <joshruehlig@gmail.com>
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Thomas Tanghus <thomas@tanghus.net>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\IImage;
abstract class Image extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
$maxSizeForImages = \OC::$server->getConfig()->getSystemValueInt('preview_max_filesize_image', 50);
$size = $file->getSize();
if ($maxSizeForImages !== -1 && $size > ($maxSizeForImages * 1024 * 1024)) {
return null;
}
$image = new \OCP\Image();
$fileName = $this->getLocalFile($file);
$image->loadFromFile($fileName);
$image->fixOrientation();
$this->cleanTmpFiles();
if ($image->valid()) {
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
return null;
}
}
@@ -0,0 +1,207 @@
<?php
/**
* @copyright Copyright (c) 2020, Nextcloud, GmbH.
*
* @author Vincent Petry <vincent@nextcloud.com>
* @author Carl Schwan <carl@carlschwan.eu>
*
* @license AGPL-3.0-or-later
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OC\StreamImage;
use OCP\Files\File;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IImage;
use OCP\Image;
use Psr\Log\LoggerInterface;
class Imaginary extends ProviderV2 {
/** @var IConfig */
private $config;
/** @var IClientService */
private $service;
/** @var LoggerInterface */
private $logger;
public function __construct(array $config) {
parent::__construct($config);
$this->config = \OC::$server->get(IConfig::class);
$this->service = \OC::$server->get(IClientService::class);
$this->logger = \OC::$server->get(LoggerInterface::class);
}
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return self::supportedMimeTypes();
}
public static function supportedMimeTypes(): string {
return '/(image\/(bmp|x-bitmap|png|jpeg|gif|heic|heif|svg\+xml|tiff|webp)|application\/(pdf|illustrator))/';
}
public function getCroppedThumbnail(File $file, int $maxX, int $maxY, bool $crop): ?IImage {
$maxSizeForImages = $this->config->getSystemValueInt('preview_max_filesize_image', 50);
$size = $file->getSize();
if ($maxSizeForImages !== -1 && $size > ($maxSizeForImages * 1024 * 1024)) {
return null;
}
$imaginaryUrl = $this->config->getSystemValueString('preview_imaginary_url', 'invalid');
if ($imaginaryUrl === 'invalid') {
$this->logger->error('Imaginary preview provider is enabled, but no url is configured. Please provide the url of your imaginary server to the \'preview_imaginary_url\' config variable.');
return null;
}
$imaginaryUrl = rtrim($imaginaryUrl, '/');
// Object store
$stream = $file->fopen('r');
if (!$stream || !is_resource($stream) || feof($stream)) {
return null;
}
$httpClient = $this->service->newClient();
$convert = false;
$autorotate = true;
switch ($file->getMimeType()) {
case 'image/heic':
// Autorotate seems to be broken for Heic so disable for that
$autorotate = false;
$mimeType = 'jpeg';
break;
case 'image/gif':
case 'image/png':
$mimeType = 'png';
break;
case 'image/svg+xml':
case 'application/pdf':
case 'application/illustrator':
$convert = true;
// Converted files do not need to be autorotated
$autorotate = false;
$mimeType = 'png';
break;
default:
$mimeType = 'jpeg';
}
$preview_format = $this->config->getSystemValueString('preview_format', 'jpeg');
switch ($preview_format) { // Change the format to the correct one
case 'webp':
$mimeType = 'webp';
break;
default:
}
$operations = [];
if ($convert) {
$operations[] = [
'operation' => 'convert',
'params' => [
'type' => $mimeType,
]
];
} elseif ($autorotate) {
$operations[] = [
'operation' => 'autorotate',
];
}
switch ($mimeType) {
case 'jpeg':
$quality = $this->config->getAppValue('preview', 'jpeg_quality', '80');
break;
case 'webp':
$quality = $this->config->getAppValue('preview', 'webp_quality', '80');
break;
default:
$quality = $this->config->getAppValue('preview', 'jpeg_quality', '80');
}
$operations[] = [
'operation' => ($crop ? 'smartcrop' : 'fit'),
'params' => [
'width' => $maxX,
'height' => $maxY,
'stripmeta' => 'true',
'type' => $mimeType,
'norotation' => 'true',
'quality' => $quality,
]
];
try {
$imaginaryKey = $this->config->getSystemValueString('preview_imaginary_key', '');
$response = $httpClient->post(
$imaginaryUrl . '/pipeline', [
'query' => ['operations' => json_encode($operations), 'key' => $imaginaryKey],
'stream' => true,
'content-type' => $file->getMimeType(),
'body' => $stream,
'nextcloud' => ['allow_local_address' => true],
'timeout' => 120,
'connect_timeout' => 3,
]);
} catch (\Throwable $e) {
$this->logger->info('Imaginary preview generation failed: ' . $e->getMessage(), [
'exception' => $e,
]);
return null;
}
if ($response->getStatusCode() !== 200) {
$this->logger->info('Imaginary preview generation failed: ' . json_decode($response->getBody())['message']);
return null;
}
// This is not optimal but previews are distorted if the wrong width and height values are
// used. Both dimension headers are only sent when passing the option "-return-size" to
// Imaginary.
if ($response->getHeader('Image-Width') && $response->getHeader('Image-Height')) {
$image = new StreamImage(
$response->getBody(),
$response->getHeader('Content-Type'),
(int)$response->getHeader('Image-Width'),
(int)$response->getHeader('Image-Height'),
);
} else {
$image = new Image();
$image->loadFromFileHandle($response->getBody());
}
return $image->valid() ? $image : null;
}
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
return $this->getCroppedThumbnail($file, $maxX, $maxY, false);
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
class JPEG extends Image {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/jpeg/';
}
}
@@ -0,0 +1,51 @@
<?php
/**
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\IImage;
class Krita extends Bundled {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/x-krita/';
}
/**
* @inheritDoc
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
$image = $this->extractThumbnail($file, 'mergedimage.png');
if (($image !== null) && $image->valid()) {
return $image;
}
$image = $this->extractThumbnail($file, 'preview.png');
if (($image !== null) && $image->valid()) {
return $image;
}
return null;
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Tanghus <thomas@tanghus.net>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\IImage;
use Psr\Log\LoggerInterface;
use wapmorgan\Mp3Info\Mp3Info;
class MP3 extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/audio\/mpeg/';
}
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
$tmpPath = $this->getLocalFile($file);
try {
$audio = new Mp3Info($tmpPath, true);
/** @var string|null|false $picture */
$picture = $audio->getCover();
} catch (\Throwable $e) {
\OC::$server->get(LoggerInterface::class)->info($e->getMessage(), [
'exception' => $e,
'app' => 'core',
]);
return null;
} finally {
$this->cleanTmpFiles();
}
if (is_string($picture)) {
$image = new \OCP\Image();
$image->loadFromData($picture);
if ($image->valid()) {
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
}
return null;
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.docm, .dotm, .xls(m), .xlt(m), .xla(m), .ppt(m), .pot(m), .pps(m), .ppa(m)
class MSOffice2003 extends Office {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/vnd.ms-.*/';
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.docx, .dotx, .xlsx, .xltx, .pptx, .potx, .ppsx
class MSOffice2007 extends Office {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/vnd.openxmlformats-officedocument.*/';
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.doc, .dot
class MSOfficeDoc extends Office {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/msword/';
}
}
@@ -0,0 +1,145 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\IImage;
class MarkDown extends TXT {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/text\/(x-)?markdown/';
}
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
$content = $file->fopen('r');
if ($content === false) {
return null;
}
$content = stream_get_contents($content, 3000);
//don't create previews of empty text files
if (trim($content) === '') {
return null;
}
// Merge text paragraph lines that might belong together
$content = preg_replace('/^(\s*)\*\s/mU', '$1- ', $content);
$content = preg_replace('/((?!^(\s*-|#)).*)(\w|\\|\.)(\r\n|\n|\r)(\w|\*)/mU', '$1 $3', $content);
// Remove markdown symbols that we cannot easily represent in rendered text in the preview
$content = preg_replace('/\*\*(.*)\*\*/U', '$1', $content);
$content = preg_replace('/\*(.*)\*/U', '$1', $content);
$content = preg_replace('/\_\_(.*)\_\_/U', '$1', $content);
$content = preg_replace('/\_(.*)\_/U', '$1', $content);
$content = preg_replace('/\~\~(.*)\~\~/U', '$1', $content);
$content = preg_replace('/\!?\[((.|\n)*)\]\((.*)\)/mU', '$1 ($3)', $content);
$content = preg_replace('/\n\n+/', "\n", $content);
$content = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $content);
$lines = preg_split("/\r\n|\n|\r/", $content);
// Define text size of text file preview
$fontSize = $maxX ? (int) ((1 / ($maxX >= 512 ? 60 : 40) * $maxX)) : 10;
$image = imagecreate($maxX, $maxY);
imagecolorallocate($image, 255, 255, 255);
$textColor = imagecolorallocate($image, 0, 0, 0);
$fontFile = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf';
$fontFileBold = __DIR__ . '/../../../core/fonts/NotoSans-Bold.ttf';
$canUseTTF = function_exists('imagettftext');
$textOffset = (int)min($maxX * 0.05, $maxY * 0.05);
$nextLineStart = 0;
$y = $textOffset;
foreach ($lines as $line) {
$actualFontSize = $fontSize;
if (mb_strpos($line, '# ') === 0) {
$actualFontSize *= 2;
}
if (mb_strpos($line, '## ') === 0) {
$actualFontSize *= 1.8;
}
if (mb_strpos($line, '### ') === 0) {
$actualFontSize *= 1.6;
}
if (mb_strpos($line, '#### ') === 0) {
$actualFontSize *= 1.4;
}
if (mb_strpos($line, '##### ') === 0) {
$actualFontSize *= 1.2;
}
if (mb_strpos($line, '###### ') === 0) {
$actualFontSize *= 1.1;
}
// Add spacing before headlines
if ($actualFontSize !== $fontSize && $y !== $textOffset) {
$y += (int)($actualFontSize * 2);
}
$x = $textOffset;
$y += (int)($nextLineStart + $actualFontSize);
if ($canUseTTF === true) {
$wordWrap = (int)((1 / $actualFontSize * 1.3) * $maxX);
// Get rid of markdown symbols that we still needed for the font size
$line = preg_replace('/^#*\s/', '', $line);
$wrappedText = wordwrap($line, $wordWrap, "\n");
$linesWrapped = count(explode("\n", $wrappedText));
imagettftext($image, $actualFontSize, 0, $x, $y, $textColor, $actualFontSize === $fontSize ? $fontFile : $fontFileBold, $wrappedText);
$nextLineStart = (int)($linesWrapped * ceil($actualFontSize * 2));
if ($actualFontSize !== $fontSize && $y !== $textOffset) {
$nextLineStart -= $actualFontSize;
}
} else {
$y -= $fontSize;
imagestring($image, 1, $x, $y, $line, $textColor);
$nextLineStart = $fontSize;
}
if ($y >= $maxY) {
break;
}
}
$imageObject = new \OCP\Image();
$imageObject->setResource($image);
return $imageObject->valid() ? $imageObject : null;
}
}
@@ -0,0 +1,98 @@
<?php
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license AGPL-3.0-or-later
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCA\Theming\ThemingDefaults;
use OCP\App\IAppManager;
use OCP\Files\IMimeTypeDetector;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\Preview\IMimeIconProvider;
class MimeIconProvider implements IMimeIconProvider {
public function __construct(
protected IMimeTypeDetector $mimetypeDetector,
protected IConfig $config,
protected IURLGenerator $urlGenerator,
protected IAppManager $appManager,
protected ThemingDefaults $themingDefaults,
) {
}
public function getMimeIconUrl(string $mime): null|string {
if (!$mime) {
return null;
}
// Fetch all the aliases
$aliases = $this->mimetypeDetector->getAllAliases();
// Remove comments
$aliases = array_filter($aliases, static function ($key) {
return !($key === '' || $key[0] === '_');
}, ARRAY_FILTER_USE_KEY);
// Map all the aliases recursively
foreach ($aliases as $alias => $value) {
if ($alias === $mime) {
$mime = $value;
}
}
$fileName = str_replace('/', '-', $mime);
if ($url = $this->searchfileName($fileName)) {
return $url;
}
$mimeType = explode('/', $mime)[0];
if ($url = $this->searchfileName($mimeType)) {
return $url;
}
return null;
}
private function searchfileName(string $fileName): null|string {
// If the file exists in the current enabled legacy
// custom theme, let's return it
$theme = $this->config->getSystemValue('theme', '');
if (!empty($theme)) {
$path = "/themes/$theme/core/img/filetypes/$fileName.svg";
if (file_exists(\OC::$SERVERROOT . $path)) {
return $this->urlGenerator->getAbsoluteURL($path);
}
}
// Previously, we used to pass this through Theming
// But it was only used to colour icons containing
// 0082c9. Since with vue we moved to inline svg icons,
// we can just use the default core icons.
// Finally, if the file exists in core, let's return it
$path = "/core/img/filetypes/$fileName.svg";
if (file_exists(\OC::$SERVERROOT . $path)) {
return $this->urlGenerator->getAbsoluteURL($path);
}
return null;
}
}
+172
View File
@@ -0,0 +1,172 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Alexander A. Klimov <grandmaster@al2klimov.de>
* @author Daniel Schneider <daniel@schneidoa.de>
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
use Psr\Log\LoggerInterface;
class Movie extends ProviderV2 {
/**
* @deprecated 23.0.0 pass option to \OCP\Preview\ProviderV2
* @var string
*/
public static $avconvBinary;
/**
* @deprecated 23.0.0 pass option to \OCP\Preview\ProviderV2
* @var string
*/
public static $ffmpegBinary;
/** @var string */
private $binary;
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/video\/.*/';
}
/**
* {@inheritDoc}
*/
public function isAvailable(FileInfo $file): bool {
// TODO: remove when avconv is dropped
if (is_null($this->binary)) {
if (isset($this->options['movieBinary'])) {
$this->binary = $this->options['movieBinary'];
} elseif (is_string(self::$avconvBinary)) {
$this->binary = self::$avconvBinary;
} elseif (is_string(self::$ffmpegBinary)) {
$this->binary = self::$ffmpegBinary;
}
}
return is_string($this->binary);
}
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
// TODO: use proc_open() and stream the source file ?
if (!$this->isAvailable($file)) {
return null;
}
$result = null;
if ($this->useTempFile($file)) {
// try downloading 5 MB first as it's likely that the first frames are present there
// in some cases this doesn't work for example when the moov atom is at the
// end of the file, so if it fails we fall back to getting the full file
$sizeAttempts = [5242880, null];
} else {
// size is irrelevant, only attempt once
$sizeAttempts = [null];
}
foreach ($sizeAttempts as $size) {
$absPath = $this->getLocalFile($file, $size);
$result = null;
if (is_string($absPath)) {
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 5);
if ($result === null) {
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 1);
if ($result === null) {
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 0);
}
}
}
$this->cleanTmpFiles();
if ($result !== null) {
break;
}
}
return $result;
}
private function generateThumbNail(int $maxX, int $maxY, string $absPath, int $second): ?IImage {
$tmpPath = \OC::$server->getTempManager()->getTemporaryFile();
$binaryType = substr(strrchr($this->binary, '/'), 1);
if ($binaryType === 'avconv') {
$cmd = [$this->binary, '-y', '-ss', (string)$second,
'-i', $absPath,
'-an', '-f', 'mjpeg', '-vframes', '1', '-vsync', '1',
$tmpPath];
} elseif ($binaryType === 'ffmpeg') {
$cmd = [$this->binary, '-y', '-ss', (string)$second,
'-i', $absPath,
'-f', 'mjpeg', '-vframes', '1',
$tmpPath];
} else {
// Not supported
unlink($tmpPath);
return null;
}
$proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes);
$returnCode = -1;
$output = "";
if (is_resource($proc)) {
$stdout = trim(stream_get_contents($pipes[1]));
$stderr = trim(stream_get_contents($pipes[2]));
$returnCode = proc_close($proc);
$output = $stdout . $stderr;
}
if ($returnCode === 0) {
$image = new \OCP\Image();
$image->loadFromFile($tmpPath);
if ($image->valid()) {
unlink($tmpPath);
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
}
if ($second === 0) {
$logger = \OC::$server->get(LoggerInterface::class);
$logger->info('Movie preview generation failed Output: {output}', ['app' => 'core', 'output' => $output]);
}
unlink($tmpPath);
return null;
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Tor Lillqvist <tml@collabora.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
use OCP\ITempManager;
use OCP\Server;
abstract class Office extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function isAvailable(FileInfo $file): bool {
return is_string($this->options['officeBinary']);
}
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
if (!$this->isAvailable($file)) {
return null;
}
$tempManager = Server::get(ITempManager::class);
// The file to generate the preview for.
$absPath = $this->getLocalFile($file);
// The destination for the LibreOffice user profile.
// LibreOffice can rune once per user profile and therefore instance id and file id are included.
$profile = $tempManager->getTemporaryFolder(
'nextcloud-office-profile-' . \OC_Util::getInstanceId() . '-' . $file->getId()
);
// The destination for the LibreOffice convert result.
$outdir = $tempManager->getTemporaryFolder(
'nextcloud-office-preview-' . \OC_Util::getInstanceId() . '-' . $file->getId()
);
if ($profile === false || $outdir === false) {
$this->cleanTmpFiles();
return null;
}
$parameters = [
$this->options['officeBinary'],
'-env:UserInstallation=file://' . escapeshellarg($profile),
'--headless',
'--nologo',
'--nofirststartwizard',
'--invisible',
'--norestore',
'--convert-to png',
'--outdir ' . escapeshellarg($outdir),
escapeshellarg($absPath),
];
$cmd = implode(' ', $parameters);
exec($cmd, $output, $returnCode);
if ($returnCode !== 0) {
$this->cleanTmpFiles();
return null;
}
$preview = $outdir . pathinfo($absPath, PATHINFO_FILENAME) . '.png';
$image = new \OCP\Image();
$image->loadFromFile($preview);
$this->cleanTmpFiles();
if ($image->valid()) {
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
return null;
}
}
@@ -0,0 +1,50 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.odt, .ott, .oth, .odm, .odg, .otg, .odp, .otp, .ods, .ots, .odc, .odf, .odb, .odi, .oxt
use OCP\Files\File;
use OCP\IImage;
class OpenDocument extends Bundled {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/vnd.oasis.opendocument.*/';
}
/**
* @inheritDoc
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
$image = $this->extractThumbnail($file, 'Thumbnails/thumbnail.png');
if (($image !== null) && $image->valid()) {
return $image;
}
return null;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.pdf
class PDF extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/pdf/';
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
class PNG extends Image {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/png/';
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.psd
class Photoshop extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/x-photoshop/';
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.eps
class Postscript extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/postscript/';
}
}
@@ -0,0 +1,69 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Preview\IProvider;
abstract class Provider implements IProvider {
private $options;
/**
* Constructor
*
* @param array $options
*/
public function __construct(array $options = []) {
$this->options = $options;
}
/**
* @return string Regex with the mimetypes that are supported by this provider
*/
abstract public function getMimeType();
/**
* Check if a preview can be generated for $path
*
* @param \OCP\Files\FileInfo $file
* @return bool
*/
public function isAvailable(\OCP\Files\FileInfo $file) {
return true;
}
/**
* Generates thumbnail which fits in $maxX and $maxY and keeps the aspect ratio, for file at path $path
*
* @param string $path Path of file
* @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image
* @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
* @param bool $scalingup Disable/Enable upscaling of previews
* @param \OC\Files\View $fileview fileview object of user folder
* @return bool|\OCP\IImage false if no preview was generated
*/
abstract public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview);
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl>
*
* @author Julius Härtl <jus@bitgrid.net>
* @author Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OC\Files\View;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
use OCP\Preview\IProvider;
use OCP\Preview\IProviderV2;
class ProviderV1Adapter implements IProviderV2 {
private $providerV1;
public function __construct(IProvider $providerV1) {
$this->providerV1 = $providerV1;
}
public function getMimeType(): string {
return (string)$this->providerV1->getMimeType();
}
public function isAvailable(FileInfo $file): bool {
return (bool)$this->providerV1->isAvailable($file);
}
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
[$view, $path] = $this->getViewAndPath($file);
$thumbnail = $this->providerV1->getThumbnail($path, $maxX, $maxY, false, $view);
return $thumbnail === false ? null: $thumbnail;
}
private function getViewAndPath(File $file) {
$view = new View(dirname($file->getPath()));
$path = $file->getName();
return [$view, $path];
}
}
@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl>
*
* @author Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
use OCP\Preview\IProviderV2;
abstract class ProviderV2 implements IProviderV2 {
/** @var array */
protected $options;
/** @var array */
protected $tmpFiles = [];
/**
* Constructor
*
* @param array $options
*/
public function __construct(array $options = []) {
$this->options = $options;
}
/**
* @return string Regex with the mimetypes that are supported by this provider
*/
abstract public function getMimeType(): string ;
/**
* Check if a preview can be generated for $path
*
* @param FileInfo $file
* @return bool
*/
public function isAvailable(FileInfo $file): bool {
return true;
}
/**
* get thumbnail for file at path $path
*
* @param File $file
* @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image
* @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
* @return null|\OCP\IImage false if no preview was generated
* @since 17.0.0
*/
abstract public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage;
protected function useTempFile(File $file): bool {
return $file->isEncrypted() || !$file->getStorage()->isLocal();
}
/**
* Get a path to either the local file or temporary file
*
* @param File $file
* @param int $maxSize maximum size for temporary files
* @return string|false
*/
protected function getLocalFile(File $file, int $maxSize = null) {
if ($this->useTempFile($file)) {
$absPath = \OC::$server->getTempManager()->getTemporaryFile();
$content = $file->fopen('r');
if ($maxSize) {
$content = stream_get_contents($content, $maxSize);
}
file_put_contents($absPath, $content);
$this->tmpFiles[] = $absPath;
return $absPath;
} else {
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
if (is_string($path)) {
return $path;
} else {
return false;
}
}
}
/**
* Clean any generated temporary files
*/
protected function cleanTmpFiles(): void {
foreach ($this->tmpFiles as $tmpFile) {
unlink($tmpFile);
}
$this->tmpFiles = [];
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
/**
* @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
//.sgi
class SGI extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/sgi/';
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\IImage;
use Psr\Log\LoggerInterface;
class SVG extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/svg\+xml/';
}
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
try {
$svg = new \Imagick();
$svg->setBackgroundColor(new \ImagickPixel('transparent'));
$content = stream_get_contents($file->fopen('r'));
if (substr($content, 0, 5) !== '<?xml') {
$content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content;
}
// Do not parse SVG files with references
if (stripos($content, 'xlink:href') !== false) {
return null;
}
$svg->readImageBlob($content);
$svg->setImageFormat('png32');
} catch (\Exception $e) {
\OC::$server->get(LoggerInterface::class)->error($e->getMessage(), [
'exception' => $e,
'app' => 'core',
]);
return null;
}
//new image object
$image = new \OCP\Image();
$image->loadFromData((string) $svg);
//check if image object is valid
if ($image->valid()) {
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
return null;
}
}
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.sxw, .stw, .sxc, .stc, .sxd, .std, .sxi, .sti, .sxg, .sxm
class StarOffice extends Office {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/application\/vnd.sun.xml.*/';
}
}
@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview\Storage;
use OC\Files\AppData\AppData;
use OC\SystemConfig;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFolder;
class Root extends AppData {
private $isMultibucketPreviewDistributionEnabled = false;
public function __construct(IRootFolder $rootFolder, SystemConfig $systemConfig) {
parent::__construct($rootFolder, $systemConfig, 'preview');
$this->isMultibucketPreviewDistributionEnabled = $systemConfig->getValue('objectstore.multibucket.preview-distribution', false) === true;
}
public function getFolder(string $name): ISimpleFolder {
$internalFolder = self::getInternalFolder($name);
try {
return parent::getFolder($internalFolder);
} catch (NotFoundException $e) {
/*
* The new folder structure is not found.
* Lets try the old one
*/
}
try {
return parent::getFolder($name);
} catch (NotFoundException $e) {
/*
* The old folder structure is not found.
* Lets try the multibucket fallback if available
*/
if ($this->isMultibucketPreviewDistributionEnabled) {
return parent::getFolder('old-multibucket/' . $internalFolder);
}
// when there is no further fallback just throw the exception
throw $e;
}
}
public function newFolder(string $name): ISimpleFolder {
$internalFolder = self::getInternalFolder($name);
return parent::newFolder($internalFolder);
}
/*
* Do not allow directory listing on this special root
* since it gets to big and time consuming
*/
public function getDirectoryListing(): array {
return [];
}
public static function getInternalFolder(string $name): string {
return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
}
public function getStorageId(): int {
return $this->getAppDataRootFolder()->getStorage()->getCache()->getNumericStorageId();
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
/**
* @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
//.tga
class TGA extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/t(ar)?ga/';
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
//.tiff
class TIFF extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/tiff/';
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Jan-Christoph Borchardt <hey@jancborchardt.net>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Nmz <nemesiz@nmz.lt>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
class TXT extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/text\/plain/';
}
/**
* {@inheritDoc}
*/
public function isAvailable(FileInfo $file): bool {
return $file->getSize() > 0;
}
/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
if (!$this->isAvailable($file)) {
return null;
}
$content = $file->fopen('r');
if ($content === false) {
return null;
}
$content = stream_get_contents($content, 3000);
//don't create previews of empty text files
if (trim($content) === '') {
return null;
}
$lines = preg_split("/\r\n|\n|\r/", $content);
// Define text size of text file preview
$fontSize = $maxX ? (int) ((1 / 32) * $maxX) : 5; //5px
$lineSize = ceil($fontSize * 1.5);
$image = imagecreate($maxX, $maxY);
imagecolorallocate($image, 255, 255, 255);
$textColor = imagecolorallocate($image, 0, 0, 0);
$fontFile = __DIR__;
$fontFile .= '/../../../core';
$fontFile .= '/fonts/NotoSans-Regular.ttf';
$canUseTTF = function_exists('imagettftext');
foreach ($lines as $index => $line) {
$index = $index + 1;
$x = 1;
$y = (int) ($index * $lineSize);
if ($canUseTTF === true) {
imagettftext($image, $fontSize, 0, $x, $y, $textColor, $fontFile, $line);
} else {
$y -= $fontSize;
imagestring($image, 1, $x, $y, $line, $textColor);
}
if (($index * $lineSize) >= $maxY) {
break;
}
}
$imageObject = new \OCP\Image();
$imageObject->setResource($image);
return $imageObject->valid() ? $imageObject : null;
}
}
@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\Files\Folder;
use OCP\Files\IAppData;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
/**
* Class Watcher
*
* @package OC\Preview
*
* Class that will watch filesystem activity and remove previews as needed.
*/
class Watcher {
/** @var IAppData */
private $appData;
/**
* Watcher constructor.
*
* @param IAppData $appData
*/
public function __construct(IAppData $appData) {
$this->appData = $appData;
}
public function postWrite(Node $node) {
$this->deleteNode($node);
}
protected function deleteNode(Node $node) {
// We only handle files
if ($node instanceof Folder) {
return;
}
try {
if (is_null($node->getId())) {
return;
}
$folder = $this->appData->getFolder((string)$node->getId());
$folder->delete();
} catch (NotFoundException $e) {
//Nothing to do
}
}
public function versionRollback(array $data) {
if (isset($data['node'])) {
$this->deleteNode($data['node']);
}
}
}
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OC\SystemConfig;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
class WatcherConnector {
/** @var IRootFolder */
private $root;
/** @var SystemConfig */
private $config;
/**
* WatcherConnector constructor.
*
* @param IRootFolder $root
* @param SystemConfig $config
*/
public function __construct(IRootFolder $root,
SystemConfig $config) {
$this->root = $root;
$this->config = $config;
}
/**
* @return Watcher
*/
private function getWatcher(): Watcher {
return \OCP\Server::get(Watcher::class);
}
public function connectWatcher() {
// Do not connect if we are not setup yet!
if ($this->config->getValue('instanceid', null) !== null) {
$this->root->listen('\OC\Files', 'postWrite', function (Node $node) {
$this->getWatcher()->postWrite($node);
});
\OC_Hook::connect('\OCP\Versions', 'rollback', $this->getWatcher(), 'versionRollback');
}
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Preview;
use OCP\Files\FileInfo;
class WebP extends Image {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/webp/';
}
public function isAvailable(FileInfo $file): bool {
return (bool)(imagetypes() & IMG_WEBP);
}
}
@@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;
class XBitmap extends Image {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/x-xbitmap/';
}
}