mirror of
https://github.com/nextcloud/server.git
synced 2026-02-03 20:41:22 -05:00
Merge pull request #55649 from nextcloud/carl/on-demand-preview-migration
feat(preview): On demand preview migration
This commit is contained in:
commit
c6c11d474b
12 changed files with 287 additions and 125 deletions
|
|
@ -38,6 +38,8 @@ class ConfigLexicon implements ILexicon {
|
|||
|
||||
public const LASTCRON_TIMESTAMP = 'lastcron';
|
||||
|
||||
public const ON_DEMAND_PREVIEW_MIGRATION = 'on_demand_preview_migration';
|
||||
|
||||
public function getStrictness(): Strictness {
|
||||
return Strictness::IGNORE;
|
||||
}
|
||||
|
|
@ -93,6 +95,12 @@ class ConfigLexicon implements ILexicon {
|
|||
new Entry(self::OCM_INVITE_ACCEPT_DIALOG, ValueType::STRING, '', 'route to local invite accept dialog', note: 'set as empty string to disable feature'),
|
||||
new Entry(self::UNIFIED_SEARCH_MIN_SEARCH_LENGTH, ValueType::INT, 1, 'Minimum search length to trigger the request', rename: 'unified-search.min-search-length'),
|
||||
new Entry(self::UNIFIED_SEARCH_MAX_RESULTS_PER_REQUEST, ValueType::INT, 25, 'Maximum results returned per search request', rename: 'unified-search.max-results-per-request'),
|
||||
new Entry(
|
||||
key: self::ON_DEMAND_PREVIEW_MIGRATION,
|
||||
type: ValueType::BOOL,
|
||||
defaultRaw: true,
|
||||
definition: 'Whether on demand preview migration is enabled.'
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1303,7 +1303,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',
|
||||
|
|
@ -1981,6 +1981,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',
|
||||
|
|
|
|||
|
|
@ -1344,7 +1344,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',
|
||||
|
|
@ -2022,6 +2022,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',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
namespace OC\Preview;
|
||||
|
||||
use OC\Core\AppInfo\ConfigLexicon;
|
||||
use OC\Preview\Db\Preview;
|
||||
use OC\Preview\Db\PreviewMapper;
|
||||
use OC\Preview\Storage\PreviewFile;
|
||||
|
|
@ -17,6 +18,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 +32,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 +112,10 @@ class Generator {
|
|||
|
||||
[$file->getId() => $previews] = $this->previewMapper->getAvailablePreviews([$file->getId()]);
|
||||
|
||||
if (empty($previews) && $this->appConfig->getValueBool('core', ConfigLexicon::ON_DEMAND_PREVIEW_MIGRATION)) {
|
||||
$previews = $this->migrateOldPreviews($file->getId());
|
||||
}
|
||||
|
||||
$previewVersion = null;
|
||||
if ($file instanceof IVersionedPreviewFile) {
|
||||
$previewVersion = $file->getPreviewVersion();
|
||||
|
|
@ -193,6 +201,21 @@ class Generator {
|
|||
return $previewFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Preview[]
|
||||
*/
|
||||
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;
|
||||
|
|
@ -516,7 +516,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