mirror of
https://github.com/nextcloud/server.git
synced 2026-02-03 20:41:22 -05:00
feat(preview): On demand preview migration
When requesting previews, which we don't find in oc_previews, search in IAppData first before creating them. Move the logic from MovepreviewJob to PreviewMigrationService and reuse that in the Preview Generator. At the same time rename MovePreviewJob to PreviewMigrationJob as it is a better name. Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
This commit is contained in:
parent
bd90e7c122
commit
6149168129
11 changed files with 278 additions and 125 deletions
107
core/BackgroundJobs/PreviewMigrationJob.php
Normal file
107
core/BackgroundJobs/PreviewMigrationJob.php
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH
|
||||
* SPDX-FileContributor: Carl Schwan
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\Core\BackgroundJobs;
|
||||
|
||||
use OC\Preview\Db\Preview;
|
||||
use OC\Preview\PreviewMigrationService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\BackgroundJob\TimedJob;
|
||||
use OCP\DB\IResult;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use Override;
|
||||
|
||||
class PreviewMigrationJob extends TimedJob {
|
||||
private string $previewRootPath;
|
||||
|
||||
public function __construct(
|
||||
ITimeFactory $time,
|
||||
private readonly IAppConfig $appConfig,
|
||||
private readonly IConfig $config,
|
||||
private readonly IDBConnection $connection,
|
||||
private readonly IRootFolder $rootFolder,
|
||||
private readonly PreviewMigrationService $migrationService,
|
||||
) {
|
||||
parent::__construct($time);
|
||||
|
||||
$this->setTimeSensitivity(self::TIME_INSENSITIVE);
|
||||
$this->setInterval(24 * 60 * 60);
|
||||
$this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function run(mixed $argument): void {
|
||||
if ($this->appConfig->getValueBool('core', 'previewMovedDone')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$startTime = time();
|
||||
while (true) {
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->select('path')
|
||||
->from('filecache')
|
||||
// Hierarchical preview folder structure
|
||||
->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
|
||||
// Legacy flat preview folder structure
|
||||
->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
|
||||
->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
|
||||
->setMaxResults(100);
|
||||
|
||||
$result = $qb->executeQuery();
|
||||
$foundPreviews = $this->processQueryResult($result);
|
||||
|
||||
if (!$foundPreviews) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop if execution time is more than one hour.
|
||||
if (time() - $startTime > 3600) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
|
||||
}
|
||||
|
||||
private function processQueryResult(IResult $result): bool {
|
||||
$foundPreview = false;
|
||||
$fileIds = [];
|
||||
$flatFileIds = [];
|
||||
while ($row = $result->fetch()) {
|
||||
$pathSplit = explode('/', $row['path']);
|
||||
assert(count($pathSplit) >= 2);
|
||||
$fileId = (int)$pathSplit[count($pathSplit) - 2];
|
||||
if (count($pathSplit) === 11) {
|
||||
// Hierarchical structure
|
||||
if (!in_array($fileId, $fileIds)) {
|
||||
$fileIds[] = $fileId;
|
||||
}
|
||||
} else {
|
||||
// Flat structure
|
||||
if (!in_array($fileId, $flatFileIds)) {
|
||||
$flatFileIds[] = $fileId;
|
||||
}
|
||||
}
|
||||
$foundPreview = true;
|
||||
}
|
||||
|
||||
foreach ($fileIds as $fileId) {
|
||||
$this->migrationService->migrateFileId($fileId, flatPath: false);
|
||||
}
|
||||
|
||||
foreach ($flatFileIds as $fileId) {
|
||||
$this->migrationService->migrateFileId($fileId, flatPath: true);
|
||||
}
|
||||
return $foundPreview;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
|
@ -18,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
|
|
|||
|
|
@ -1299,7 +1299,7 @@ return array(
|
|||
'OC\\Core\\BackgroundJobs\\CleanupLoginFlowV2' => $baseDir . '/core/BackgroundJobs/CleanupLoginFlowV2.php',
|
||||
'OC\\Core\\BackgroundJobs\\GenerateMetadataJob' => $baseDir . '/core/BackgroundJobs/GenerateMetadataJob.php',
|
||||
'OC\\Core\\BackgroundJobs\\LookupServerSendCheckBackgroundJob' => $baseDir . '/core/BackgroundJobs/LookupServerSendCheckBackgroundJob.php',
|
||||
'OC\\Core\\BackgroundJobs\\MovePreviewJob' => $baseDir . '/core/BackgroundJobs/MovePreviewJob.php',
|
||||
'OC\\Core\\BackgroundJobs\\PreviewMigrationJob' => $baseDir . '/core/BackgroundJobs/PreviewMigrationJob.php',
|
||||
'OC\\Core\\Command\\App\\Disable' => $baseDir . '/core/Command/App/Disable.php',
|
||||
'OC\\Core\\Command\\App\\Enable' => $baseDir . '/core/Command/App/Enable.php',
|
||||
'OC\\Core\\Command\\App\\GetPath' => $baseDir . '/core/Command/App/GetPath.php',
|
||||
|
|
@ -1977,6 +1977,7 @@ return array(
|
|||
'OC\\Preview\\PNG' => $baseDir . '/lib/private/Preview/PNG.php',
|
||||
'OC\\Preview\\Photoshop' => $baseDir . '/lib/private/Preview/Photoshop.php',
|
||||
'OC\\Preview\\Postscript' => $baseDir . '/lib/private/Preview/Postscript.php',
|
||||
'OC\\Preview\\PreviewMigrationService' => $baseDir . '/lib/private/Preview/PreviewMigrationService.php',
|
||||
'OC\\Preview\\PreviewService' => $baseDir . '/lib/private/Preview/PreviewService.php',
|
||||
'OC\\Preview\\ProviderV2' => $baseDir . '/lib/private/Preview/ProviderV2.php',
|
||||
'OC\\Preview\\SGI' => $baseDir . '/lib/private/Preview/SGI.php',
|
||||
|
|
|
|||
|
|
@ -1340,7 +1340,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\BackgroundJobs\\CleanupLoginFlowV2' => __DIR__ . '/../../..' . '/core/BackgroundJobs/CleanupLoginFlowV2.php',
|
||||
'OC\\Core\\BackgroundJobs\\GenerateMetadataJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/GenerateMetadataJob.php',
|
||||
'OC\\Core\\BackgroundJobs\\LookupServerSendCheckBackgroundJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/LookupServerSendCheckBackgroundJob.php',
|
||||
'OC\\Core\\BackgroundJobs\\MovePreviewJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/MovePreviewJob.php',
|
||||
'OC\\Core\\BackgroundJobs\\PreviewMigrationJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/PreviewMigrationJob.php',
|
||||
'OC\\Core\\Command\\App\\Disable' => __DIR__ . '/../../..' . '/core/Command/App/Disable.php',
|
||||
'OC\\Core\\Command\\App\\Enable' => __DIR__ . '/../../..' . '/core/Command/App/Enable.php',
|
||||
'OC\\Core\\Command\\App\\GetPath' => __DIR__ . '/../../..' . '/core/Command/App/GetPath.php',
|
||||
|
|
@ -2018,6 +2018,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Preview\\PNG' => __DIR__ . '/../../..' . '/lib/private/Preview/PNG.php',
|
||||
'OC\\Preview\\Photoshop' => __DIR__ . '/../../..' . '/lib/private/Preview/Photoshop.php',
|
||||
'OC\\Preview\\Postscript' => __DIR__ . '/../../..' . '/lib/private/Preview/Postscript.php',
|
||||
'OC\\Preview\\PreviewMigrationService' => __DIR__ . '/../../..' . '/lib/private/Preview/PreviewMigrationService.php',
|
||||
'OC\\Preview\\PreviewService' => __DIR__ . '/../../..' . '/lib/private/Preview/PreviewService.php',
|
||||
'OC\\Preview\\ProviderV2' => __DIR__ . '/../../..' . '/lib/private/Preview/ProviderV2.php',
|
||||
'OC\\Preview\\SGI' => __DIR__ . '/../../..' . '/lib/private/Preview/SGI.php',
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use OCP\Files\NotFoundException;
|
|||
use OCP\Files\NotPermittedException;
|
||||
use OCP\Files\SimpleFS\InMemoryFile;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IImage;
|
||||
use OCP\IPreview;
|
||||
|
|
@ -30,13 +31,15 @@ class Generator {
|
|||
public const SEMAPHORE_ID_NEW = 0x07ea;
|
||||
|
||||
public function __construct(
|
||||
private IConfig $config,
|
||||
private IPreview $previewManager,
|
||||
private GeneratorHelper $helper,
|
||||
private IEventDispatcher $eventDispatcher,
|
||||
private LoggerInterface $logger,
|
||||
private PreviewMapper $previewMapper,
|
||||
private StorageFactory $storageFactory,
|
||||
private readonly IConfig $config,
|
||||
private readonly IAppConfig $appConfig,
|
||||
private readonly IPreview $previewManager,
|
||||
private readonly GeneratorHelper $helper,
|
||||
private readonly IEventDispatcher $eventDispatcher,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly PreviewMapper $previewMapper,
|
||||
private readonly StorageFactory $storageFactory,
|
||||
private readonly PreviewMigrationService $migrationService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -108,6 +111,10 @@ class Generator {
|
|||
|
||||
[$file->getId() => $previews] = $this->previewMapper->getAvailablePreviews([$file->getId()]);
|
||||
|
||||
if (empty($previews)) {
|
||||
$previews = $this->migrateOldPreviews($file->getId());
|
||||
}
|
||||
|
||||
$previewVersion = null;
|
||||
if ($file instanceof IVersionedPreviewFile) {
|
||||
$previewVersion = $file->getPreviewVersion();
|
||||
|
|
@ -193,6 +200,21 @@ class Generator {
|
|||
return $previewFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string|int, string[]>
|
||||
*/
|
||||
private function migrateOldPreviews(int $fileId): array {
|
||||
if ($this->appConfig->getValueBool('core', 'previewMovedDone')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$previews = $this->migrationService->migrateFileId($fileId, flatPath: false);
|
||||
if (empty($previews)) {
|
||||
$previews = $this->migrationService->migrateFileId($fileId, flatPath: true);
|
||||
}
|
||||
return $previews;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -2,130 +2,60 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH
|
||||
* SPDX-FileContributor: Carl Schwan
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\Core\BackgroundJobs;
|
||||
namespace OC\Preview;
|
||||
|
||||
use OC\Files\SimpleFS\SimpleFile;
|
||||
use OC\Preview\Db\Preview;
|
||||
use OC\Preview\Db\PreviewMapper;
|
||||
use OC\Preview\Storage\StorageFactory;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\BackgroundJob\TimedJob;
|
||||
use OCP\DB\Exception;
|
||||
use OCP\DB\IResult;
|
||||
use OCP\Files\AppData\IAppDataFactory;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\Files\IMimeTypeDetector;
|
||||
use OCP\Files\IMimeTypeLoader;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use Override;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class MovePreviewJob extends TimedJob {
|
||||
class PreviewMigrationService {
|
||||
private IAppData $appData;
|
||||
private string $previewRootPath;
|
||||
|
||||
public function __construct(
|
||||
ITimeFactory $time,
|
||||
private readonly IAppConfig $appConfig,
|
||||
private readonly IConfig $config,
|
||||
private readonly PreviewMapper $previewMapper,
|
||||
private readonly StorageFactory $storageFactory,
|
||||
private readonly IDBConnection $connection,
|
||||
private readonly IRootFolder $rootFolder,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly IMimeTypeDetector $mimeTypeDetector,
|
||||
private readonly IMimeTypeLoader $mimeTypeLoader,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly IDBConnection $connection,
|
||||
private readonly PreviewMapper $previewMapper,
|
||||
private readonly StorageFactory $storageFactory,
|
||||
IAppDataFactory $appDataFactory,
|
||||
) {
|
||||
parent::__construct($time);
|
||||
|
||||
$this->appData = $appDataFactory->get('preview');
|
||||
$this->setTimeSensitivity(self::TIME_INSENSITIVE);
|
||||
$this->setInterval(24 * 60 * 60);
|
||||
$this->previewRootPath = 'appdata_' . $this->config->getSystemValueString('instanceid') . '/preview/';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function run(mixed $argument): void {
|
||||
if ($this->appConfig->getValueBool('core', 'previewMovedDone')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$startTime = time();
|
||||
while (true) {
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->select('path')
|
||||
->from('filecache')
|
||||
// Hierarchical preview folder structure
|
||||
->where($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%/%/%/%/%/%/%/%')))
|
||||
// Legacy flat preview folder structure
|
||||
->orWhere($qb->expr()->like('path', $qb->createNamedParameter($this->previewRootPath . '%/%.%')))
|
||||
->hintShardKey('storage', $this->rootFolder->getMountPoint()->getNumericStorageId())
|
||||
->setMaxResults(100);
|
||||
|
||||
$result = $qb->executeQuery();
|
||||
$foundPreviews = $this->processQueryResult($result);
|
||||
|
||||
if (!$foundPreviews) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop if execution time is more than one hour.
|
||||
if (time() - $startTime > 3600) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
|
||||
}
|
||||
|
||||
private function processQueryResult(IResult $result): bool {
|
||||
$foundPreview = false;
|
||||
$fileIds = [];
|
||||
$flatFileIds = [];
|
||||
while ($row = $result->fetchAssociative()) {
|
||||
$pathSplit = explode('/', $row['path']);
|
||||
assert(count($pathSplit) >= 2);
|
||||
$fileId = (int)$pathSplit[count($pathSplit) - 2];
|
||||
if (count($pathSplit) === 11) {
|
||||
// Hierarchical structure
|
||||
if (!in_array($fileId, $fileIds)) {
|
||||
$fileIds[] = $fileId;
|
||||
}
|
||||
} else {
|
||||
// Flat structure
|
||||
if (!in_array($fileId, $flatFileIds)) {
|
||||
$flatFileIds[] = $fileId;
|
||||
}
|
||||
}
|
||||
$foundPreview = true;
|
||||
}
|
||||
|
||||
foreach ($fileIds as $fileId) {
|
||||
$this->processPreviews($fileId, flatPath: false);
|
||||
}
|
||||
|
||||
foreach ($flatFileIds as $fileId) {
|
||||
$this->processPreviews($fileId, flatPath: true);
|
||||
}
|
||||
return $foundPreview;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string|int, string[]> $previewFolders
|
||||
* @return Preview[]
|
||||
*/
|
||||
private function processPreviews(int $fileId, bool $flatPath): void {
|
||||
public function migrateFileId(int $fileId, bool $flatPath): array {
|
||||
$previews = [];
|
||||
$internalPath = $this->getInternalFolder((string)$fileId, $flatPath);
|
||||
$folder = $this->appData->getFolder($internalPath);
|
||||
try {
|
||||
$folder = $this->appData->getFolder($internalPath);
|
||||
} catch (NotFoundException) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @var list<array{file: SimpleFile, preview: Preview}> $previewFiles
|
||||
|
|
@ -152,6 +82,10 @@ class MovePreviewJob extends TimedJob {
|
|||
];
|
||||
}
|
||||
|
||||
if (empty($previewFiles)) {
|
||||
return $previews;
|
||||
}
|
||||
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->select('storage', 'etag', 'mimetype')
|
||||
->from('filecache')
|
||||
|
|
@ -194,6 +128,8 @@ class MovePreviewJob extends TimedJob {
|
|||
$this->previewMapper->delete($preview);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$previews[] = $preview;
|
||||
}
|
||||
} else {
|
||||
// No matching fileId, delete preview
|
||||
|
|
@ -211,9 +147,11 @@ class MovePreviewJob extends TimedJob {
|
|||
}
|
||||
|
||||
$this->deleteFolder($internalPath);
|
||||
|
||||
return $previews;
|
||||
}
|
||||
|
||||
public static function getInternalFolder(string $name, bool $flatPath): string {
|
||||
private static function getInternalFolder(string $name, bool $flatPath): string {
|
||||
if ($flatPath) {
|
||||
return $name;
|
||||
}
|
||||
|
|
@ -13,12 +13,14 @@ use OC\Preview\Db\PreviewMapper;
|
|||
use OC\Preview\Generator;
|
||||
use OC\Preview\GeneratorHelper;
|
||||
use OC\Preview\IMagickSupport;
|
||||
use OC\Preview\PreviewMigrationService;
|
||||
use OC\Preview\Storage\StorageFactory;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IBinaryFinder;
|
||||
use OCP\IConfig;
|
||||
use OCP\IPreview;
|
||||
|
|
@ -135,12 +137,14 @@ class PreviewManager implements IPreview {
|
|||
if ($this->generator === null) {
|
||||
$this->generator = new Generator(
|
||||
$this->config,
|
||||
$this->container->get(IAppConfig::class),
|
||||
$this,
|
||||
new GeneratorHelper(),
|
||||
$this->eventDispatcher,
|
||||
$this->container->get(LoggerInterface::class),
|
||||
$this->container->get(PreviewMapper::class),
|
||||
$this->container->get(StorageFactory::class),
|
||||
$this->container->get(PreviewMigrationService::class),
|
||||
);
|
||||
}
|
||||
return $this->generator;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
namespace OC\Repair;
|
||||
|
||||
use OC\Core\BackgroundJobs\MovePreviewJob;
|
||||
use OC\Core\BackgroundJobs\PreviewMigrationJob;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
|
|
@ -22,6 +22,6 @@ class AddMovePreviewJob implements IRepairStep {
|
|||
}
|
||||
|
||||
public function run(IOutput $output) {
|
||||
$this->jobList->add(MovePreviewJob::class);
|
||||
$this->jobList->add(PreviewMigrationJob::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use InvalidArgumentException;
|
|||
use OC\Authentication\Token\PublicKeyTokenProvider;
|
||||
use OC\Authentication\Token\TokenCleanupJob;
|
||||
use OC\Core\BackgroundJobs\GenerateMetadataJob;
|
||||
use OC\Core\BackgroundJobs\MovePreviewJob;
|
||||
use OC\Core\BackgroundJobs\PreviewMigrationJob;
|
||||
use OC\Log\Rotate;
|
||||
use OC\Preview\BackgroundCleanupJob;
|
||||
use OC\TextProcessing\RemoveOldTasksBackgroundJob;
|
||||
|
|
@ -506,7 +506,7 @@ class Setup {
|
|||
$jobList->add(RemoveOldTasksBackgroundJob::class);
|
||||
$jobList->add(CleanupDeletedUsers::class);
|
||||
$jobList->add(GenerateMetadataJob::class);
|
||||
$jobList->add(MovePreviewJob::class);
|
||||
$jobList->add(PreviewMigrationJob::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,15 +7,18 @@
|
|||
|
||||
namespace Test\Preview;
|
||||
|
||||
use OC\Core\AppInfo\ConfigLexicon;
|
||||
use OC\Preview\Db\Preview;
|
||||
use OC\Preview\Db\PreviewMapper;
|
||||
use OC\Preview\Generator;
|
||||
use OC\Preview\GeneratorHelper;
|
||||
use OC\Preview\PreviewMigrationService;
|
||||
use OC\Preview\Storage\StorageFactory;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Mount\IMountPoint;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IImage;
|
||||
use OCP\IPreview;
|
||||
|
|
@ -34,6 +37,7 @@ abstract class VersionedPreviewFile implements IVersionedPreviewFile, File {
|
|||
|
||||
class GeneratorTest extends TestCase {
|
||||
private IConfig&MockObject $config;
|
||||
private IAppConfig&MockObject $appConfig;
|
||||
private IPreview&MockObject $previewManager;
|
||||
private GeneratorHelper&MockObject $helper;
|
||||
private IEventDispatcher&MockObject $eventDispatcher;
|
||||
|
|
@ -41,26 +45,31 @@ class GeneratorTest extends TestCase {
|
|||
private LoggerInterface&MockObject $logger;
|
||||
private StorageFactory&MockObject $storageFactory;
|
||||
private PreviewMapper&MockObject $previewMapper;
|
||||
private PreviewMigrationService&MockObject $migrationService;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->previewManager = $this->createMock(IPreview::class);
|
||||
$this->helper = $this->createMock(GeneratorHelper::class);
|
||||
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->previewMapper = $this->createMock(PreviewMapper::class);
|
||||
$this->storageFactory = $this->createMock(StorageFactory::class);
|
||||
$this->migrationService = $this->createMock(PreviewMigrationService::class);
|
||||
|
||||
$this->generator = new Generator(
|
||||
$this->config,
|
||||
$this->appConfig,
|
||||
$this->previewManager,
|
||||
$this->helper,
|
||||
$this->eventDispatcher,
|
||||
$this->logger,
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
$this->migrationService,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -247,6 +256,63 @@ class GeneratorTest extends TestCase {
|
|||
$this->assertSame(1000, $result->getSize());
|
||||
}
|
||||
|
||||
|
||||
public function testMigrateOldPreview(): void {
|
||||
$file = $this->getFile(42, 'myMimeType', false);
|
||||
|
||||
$maxPreview = new Preview();
|
||||
$maxPreview->setWidth(1000);
|
||||
$maxPreview->setHeight(1000);
|
||||
$maxPreview->setMax(true);
|
||||
$maxPreview->setSize(1000);
|
||||
$maxPreview->setCropped(false);
|
||||
$maxPreview->setStorageId(1);
|
||||
$maxPreview->setVersion(null);
|
||||
$maxPreview->setMimeType('image/png');
|
||||
|
||||
$previewFile = new Preview();
|
||||
$previewFile->setWidth(256);
|
||||
$previewFile->setHeight(256);
|
||||
$previewFile->setMax(false);
|
||||
$previewFile->setSize(1000);
|
||||
$previewFile->setVersion(null);
|
||||
$previewFile->setCropped(false);
|
||||
$previewFile->setStorageId(1);
|
||||
$previewFile->setMimeType('image/png');
|
||||
|
||||
$this->previewManager->method('isMimeSupported')
|
||||
->with($this->equalTo('myMimeType'))
|
||||
->willReturn(true);
|
||||
|
||||
$this->previewMapper->method('getAvailablePreviews')
|
||||
->with($this->equalTo([42]))
|
||||
->willReturn([42 => []]);
|
||||
|
||||
$this->config->method('getSystemValueString')
|
||||
->willReturnCallback(function ($key, $default) {
|
||||
return $default;
|
||||
});
|
||||
|
||||
$this->config->method('getSystemValueInt')
|
||||
->willReturnCallback(function ($key, $default) {
|
||||
return $default;
|
||||
});
|
||||
|
||||
$this->appConfig->method('getValueBool')
|
||||
->willReturnCallback(fn ($app, $key, $default) => match ($key) {
|
||||
ConfigLexicon::ON_DEMAND_PREVIEW_MIGRATION => true,
|
||||
'previewMovedDone' => false,
|
||||
});
|
||||
|
||||
$this->migrationService->expects($this->exactly(1))
|
||||
->method('migrateFileId')
|
||||
->willReturn([$maxPreview, $previewFile]);
|
||||
|
||||
$result = $this->generator->getPreview($file, 100, 100);
|
||||
$this->assertSame('256-256.png', $result->getName());
|
||||
$this->assertSame(1000, $result->getSize());
|
||||
}
|
||||
|
||||
public function testInvalidMimeType(): void {
|
||||
$this->expectException(NotFoundException::class);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace lib\Preview;
|
||||
|
||||
use OC\Core\BackgroundJobs\MovePreviewJob;
|
||||
use OC\Core\BackgroundJobs\PreviewMigrationJob;
|
||||
use OC\Preview\Db\PreviewMapper;
|
||||
use OC\Preview\PreviewMigrationService;
|
||||
use OC\Preview\PreviewService;
|
||||
use OC\Preview\Storage\StorageFactory;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
|
|
@ -30,7 +31,7 @@ use Psr\Log\LoggerInterface;
|
|||
use Test\TestCase;
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\Group('DB')]
|
||||
class MovePreviewJobTest extends TestCase {
|
||||
class PreviewMigrationJobTest extends TestCase {
|
||||
private IAppData $previewAppData;
|
||||
private PreviewMapper $previewMapper;
|
||||
private IAppConfig&MockObject $appConfig;
|
||||
|
|
@ -112,18 +113,23 @@ class MovePreviewJobTest extends TestCase {
|
|||
$this->assertEquals(2, count($folder->getDirectoryListing()));
|
||||
$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
|
||||
|
||||
$job = new MovePreviewJob(
|
||||
$job = new PreviewMigrationJob(
|
||||
Server::get(ITimeFactory::class),
|
||||
$this->appConfig,
|
||||
$this->config,
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
Server::get(IDBConnection::class),
|
||||
Server::get(IRootFolder::class),
|
||||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
$this->logger,
|
||||
Server::get(IAppDataFactory::class),
|
||||
new PreviewMigrationService(
|
||||
$this->config,
|
||||
Server::get(IRootFolder::class),
|
||||
$this->logger,
|
||||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
Server::get(IDBConnection::class),
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
Server::get(IAppDataFactory::class),
|
||||
)
|
||||
);
|
||||
$this->invokePrivate($job, 'run', [[]]);
|
||||
$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
|
||||
|
|
@ -144,18 +150,23 @@ class MovePreviewJobTest extends TestCase {
|
|||
$this->assertEquals(2, count($folder->getDirectoryListing()));
|
||||
$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
|
||||
|
||||
$job = new MovePreviewJob(
|
||||
$job = new PreviewMigrationJob(
|
||||
Server::get(ITimeFactory::class),
|
||||
$this->appConfig,
|
||||
$this->config,
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
Server::get(IDBConnection::class),
|
||||
Server::get(IRootFolder::class),
|
||||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
$this->logger,
|
||||
Server::get(IAppDataFactory::class)
|
||||
new PreviewMigrationService(
|
||||
$this->config,
|
||||
Server::get(IRootFolder::class),
|
||||
$this->logger,
|
||||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
Server::get(IDBConnection::class),
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
Server::get(IAppDataFactory::class),
|
||||
)
|
||||
);
|
||||
$this->invokePrivate($job, 'run', [[]]);
|
||||
$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
|
||||
|
|
@ -184,18 +195,23 @@ class MovePreviewJobTest extends TestCase {
|
|||
$this->assertEquals(9, count($folder->getDirectoryListing()));
|
||||
$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
|
||||
|
||||
$job = new MovePreviewJob(
|
||||
$job = new PreviewMigrationJob(
|
||||
Server::get(ITimeFactory::class),
|
||||
$this->appConfig,
|
||||
$this->config,
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
Server::get(IDBConnection::class),
|
||||
Server::get(IRootFolder::class),
|
||||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
$this->logger,
|
||||
Server::get(IAppDataFactory::class)
|
||||
new PreviewMigrationService(
|
||||
$this->config,
|
||||
Server::get(IRootFolder::class),
|
||||
$this->logger,
|
||||
$this->mimeTypeDetector,
|
||||
$this->mimeTypeLoader,
|
||||
Server::get(IDBConnection::class),
|
||||
$this->previewMapper,
|
||||
$this->storageFactory,
|
||||
Server::get(IAppDataFactory::class),
|
||||
)
|
||||
);
|
||||
$this->invokePrivate($job, 'run', [[]]);
|
||||
$previews = iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5));
|
||||
Loading…
Reference in a new issue