mirror of
https://github.com/nextcloud/server.git
synced 2026-09-30 14:17:52 +02:00
Signed-off-by: Carl Schwan <carl@carlschwan.eu> Assisted-by: ClaudeCode:claude-sonnet-5
111 lines
3.4 KiB
PHP
111 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace OC\Core\BackgroundJobs;
|
|
|
|
use OC\Preview\PreviewMigrationService;
|
|
use OCP\AppFramework\Utility\ITimeFactory;
|
|
use OCP\BackgroundJob\TimedJob;
|
|
use OCP\Files\FileInfo;
|
|
use OCP\Files\IRootFolder;
|
|
use OCP\IAppConfig;
|
|
use OCP\IConfig;
|
|
use Override;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class PreviewMigrationJob extends TimedJob {
|
|
private string $previewRootPath;
|
|
|
|
public function __construct(
|
|
ITimeFactory $time,
|
|
private readonly IAppConfig $appConfig,
|
|
private readonly IConfig $config,
|
|
private readonly IRootFolder $rootFolder,
|
|
private readonly PreviewMigrationService $migrationService,
|
|
private readonly LoggerInterface $logger,
|
|
) {
|
|
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;
|
|
}
|
|
|
|
$storage = $this->rootFolder->getMountPoint()->getStorage();
|
|
if ($storage === null) {
|
|
$this->logger->warning('Preview migration skipped: the root mount point has no storage.');
|
|
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
|
|
return;
|
|
}
|
|
|
|
$cache = $storage->getCache();
|
|
$previewRootId = $cache->getId(rtrim($this->previewRootPath, '/'));
|
|
if ($previewRootId === -1) {
|
|
// No previews were ever generated, or the storage config no longer
|
|
// matches the one the filecache data was recorded under.
|
|
$this->logger->warning('Preview migration skipped: no preview root found at "{path}" on storage "{storageId}".', [
|
|
'path' => $this->previewRootPath,
|
|
'storageId' => $storage->getId(),
|
|
]);
|
|
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
|
|
return;
|
|
}
|
|
|
|
$startTime = time();
|
|
|
|
// Walk the preview folder tree via the `parent` column, which is indexed on
|
|
// every supported database platform.
|
|
//
|
|
// Depth from the preview root tells us which structure a leaf folder holds:
|
|
// - depth 1: legacy flat structure, e.g. preview/<fileid>/<size>.png
|
|
// - depth 8: hierarchical structure, e.g. preview/a/b/c/d/e/f/g/<fileid>/<size>.png
|
|
$foldersToVisit = [[$previewRootId, '', 0]];
|
|
|
|
while ($foldersToVisit !== []) {
|
|
[$folderId, $folderName, $depth] = array_pop($foldersToVisit);
|
|
|
|
// Collect the actual preview files here so migrateFileId() doesn't need to
|
|
// list this folder's contents a second time.
|
|
$previewEntries = [];
|
|
foreach ($cache->getFolderContentsById($folderId) as $entry) {
|
|
if ($entry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
|
|
$foldersToVisit[] = [$entry->getId(), $entry->getName(), $depth + 1];
|
|
} else {
|
|
$previewEntries[] = $entry;
|
|
}
|
|
}
|
|
|
|
if ($previewEntries === [] || !ctype_digit($folderName)) {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
$this->migrationService->migrateFileId((int)$folderName, flatPath: $depth === 1, entries: $previewEntries);
|
|
} catch (\Exception $e) {
|
|
$this->logger->error('Failed to migrate preview with fileId: ' . $folderName, [
|
|
'exception' => $e,
|
|
]);
|
|
}
|
|
|
|
// Stop if execution time is more than one hour.
|
|
if (time() - $startTime > 3600) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
$this->appConfig->setValueBool('core', 'previewMovedDone', true);
|
|
}
|
|
}
|