Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/AppInfo/ConfigLexicon.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -93,6 +95,12 @@ public function getAppConfigs(): array {
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.'
),
];
}

Expand Down
107 changes: 107 additions & 0 deletions core/BackgroundJobs/PreviewMigrationJob.php
Original file line number Diff line number Diff line change
@@ -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;
}
}
2 changes: 0 additions & 2 deletions lib/composer/composer/LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Copyright (c) Nils Adermann, Jordi Boggiano

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand All @@ -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.

3 changes: 2 additions & 1 deletion lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@
'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',
Expand Down Expand Up @@ -1977,6 +1977,7 @@
'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',
Expand Down
3 changes: 2 additions & 1 deletion lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
37 changes: 30 additions & 7 deletions lib/private/Preview/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,6 +18,7 @@
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;
Expand All @@ -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,
) {
}

Expand Down Expand Up @@ -108,6 +112,10 @@ public function generatePreviews(File $file, array $specifications, ?string $mim

[$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();
Expand Down Expand Up @@ -193,6 +201,21 @@ public function generatePreviews(File $file, array $specifications, ?string $mim
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
Expand Down
Loading
Loading