Skip to content

Commit

Permalink
Preload the files metadata and cache photos-size
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <coding@schilljs.com>
  • Loading branch information
nickvergessen committed Dec 4, 2023
1 parent 51edc88 commit e2c87ff
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 17 deletions.
22 changes: 8 additions & 14 deletions lib/Chat/Parser/SystemMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Share\Helper\FilesMetadataCache;
use OCA\Talk\Share\RoomShareProvider;
use OCP\Comments\IComment;
use OCP\EventDispatcher\Event;
Expand Down Expand Up @@ -88,8 +89,7 @@ public function __construct(
protected IRootFolder $rootFolder,
protected ICloudIdManager $cloudIdManager,
protected IURLGenerator $url,
protected IRequest $request,
protected IFilesMetadataManager $metadataManager,
protected FilesMetadataCache $metadataCache,
) {
}

Expand Down Expand Up @@ -741,20 +741,14 @@ protected function getFileFromShare(?Participant $participant, string $shareId):
];

// If a preview is available, check if we can get the dimensions of the file from the metadata API
if ($isPreviewAvailable
&& $this->request->isUserAgent([IRequest::USER_AGENT_TALK_IOS])) {
if ($isPreviewAvailable && str_starts_with($node->getMimeType(), 'image/')) {
try {
$metadata = $this->metadataManager->getMetaData($fileId, false);

if ($metadata->hasKey('photos-size')) {
$sizeMetadata = $metadata->getArray('photos-size');

if (isset($sizeMetadata['width']) && isset($sizeMetadata['height'])) {
$data['width'] = $sizeMetadata['width'];
$data['height'] = $sizeMetadata['height'];
}
$sizeMetadata = $this->metadataCache->getMetadataPhotosSizeForFileId($fileId);
if (isset($sizeMetadata['width'], $sizeMetadata['height'])) {
$data['width'] = $sizeMetadata['width'];
$data['height'] = $sizeMetadata['height'];
}
} catch (FilesMetadataNotFoundException $e) {
} catch (FilesMetadataNotFoundException) {
}
}

Expand Down
14 changes: 11 additions & 3 deletions lib/Controller/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\ReminderService;
use OCA\Talk\Service\SessionService;
use OCA\Talk\Share\Helper\FilesMetadataCache;
use OCA\Talk\Share\RoomShareProvider;
use OCP\App\IAppManager;
use OCP\AppFramework\Db\DoesNotExistException;
Expand All @@ -73,6 +74,7 @@
use OCP\RichObjectStrings\IValidator;
use OCP\Security\ITrustedDomainHelper;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IShare;
use OCP\User\Events\UserLiveStatusEvent;
use OCP\UserStatus\IManager as IUserStatusManager;
use OCP\UserStatus\IUserStatus;
Expand Down Expand Up @@ -103,6 +105,7 @@ public function __construct(
private GuestManager $guestManager,
private MessageParser $messageParser,
protected RoomShareProvider $shareProvider,
protected FilesMetadataCache $filesMetadataCache,
private IManager $autoCompleteManager,
private IUserStatusManager $statusManager,
protected MatterbridgeManager $matterbridgeManager,
Expand Down Expand Up @@ -308,7 +311,8 @@ public function shareObjectToChat(string $objectType, string $objectId, string $

/*
* Gather share IDs from the comments and preload share definitions
* to avoid separate database query for each individual share.
* and files metadata to avoid separate database query for each
* individual share/node later on.
*
* @param IComment[] $comments
*/
Expand All @@ -326,10 +330,14 @@ protected function preloadShares(array $comments): void {
}
}
if (!empty($shareIds)) {
// Ignore the result for now. Retrieved Share objects will be cached by
// Retrieved Share objects will be cached by
// the RoomShareProvider and returned from the cache to
// the Parser\SystemMessage without additional database queries.
$this->shareProvider->getSharesByIds($shareIds);
$shares = $this->shareProvider->getSharesByIds($shareIds);

// Preload files metadata as well
$fileIds = array_filter(array_map(static fn(IShare $share) => $share->getNodeId(), $shares));
$this->filesMetadataCache->preloadMetadata($fileIds);
}
}

Expand Down
102 changes: 102 additions & 0 deletions lib/Share/Helper/FilesMetadataCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.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 OCA\Talk\Share\Helper;

use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException;
use OCP\FilesMetadata\IFilesMetadataManager;
use OCP\FilesMetadata\Model\IFilesMetadata;

class FilesMetadataCache {
/** @var array<int, array{width: int, height: int}> */
protected array $filesSizeData = [];

public function __construct(
protected IFilesMetadataManager $filesMetadataManager,
) {
}

/**
* @param list<int> $fileIds
*/
public function preloadMetadata(array $fileIds): void {
$missingFileIds = array_diff($fileIds, array_keys($this->filesSizeData));
if (empty($missingFileIds)) {
return;
}

$data = $this->filesMetadataManager->getMetadataForFiles($missingFileIds);

Check failure on line 52 in lib/Share/Helper/FilesMetadataCache.php

View workflow job for this annotation

GitHub Actions / Nextcloud

UndefinedInterfaceMethod

lib/Share/Helper/FilesMetadataCache.php:52:40: UndefinedInterfaceMethod: Method OCP\FilesMetadata\IFilesMetadataManager::getMetadataForFiles does not exist (see https://psalm.dev/181)
foreach ($data as $fileId => $metadata) {
$this->cachePhotosSize($fileId, $metadata);
}
}

/**
* @return array
* @psalm-return array{width: int, height: int}
* @throws FilesMetadataNotFoundException
*/
public function getMetadataPhotosSizeForFileId(int $fileId): array {
if (!array_key_exists($fileId, $this->filesSizeData)) {
try {
$this->cachePhotosSize($fileId, $this->filesMetadataManager->getMetadata($fileId, true));
} catch (FilesMetadataNotFoundException) {
$this->filesSizeData[$fileId] = null;

Check failure on line 68 in lib/Share/Helper/FilesMetadataCache.php

View workflow job for this annotation

GitHub Actions / Nextcloud

InvalidPropertyAssignmentValue

lib/Share/Helper/FilesMetadataCache.php:68:5: InvalidPropertyAssignmentValue: $this->filesSizeData with declared type 'array<int, array{height: int, width: int}>' cannot be assigned type 'non-empty-array<int, array{height: int, width: int}|null>' (see https://psalm.dev/145)
}
}

if (!isset($this->filesSizeData[$fileId])) {
return throw new FilesMetadataNotFoundException();

Check failure on line 73 in lib/Share/Helper/FilesMetadataCache.php

View workflow job for this annotation

GitHub Actions / Nextcloud

NoValue

lib/Share/Helper/FilesMetadataCache.php:73:4: NoValue: All possible types for this return were invalidated - This may be dead code (see https://psalm.dev/179)
}

return $this->filesSizeData[$fileId];
}

protected function cachePhotosSize(int $fileId, IFilesMetadata $metadata): void {
if ($metadata->hasKey('photos-size')) {
try {
$sizeMetadata = $metadata->getArray('photos-size');
} catch (FilesMetadataNotFoundException|FilesMetadataTypeException) {
$this->filesSizeData[$fileId] = null;

Check failure on line 84 in lib/Share/Helper/FilesMetadataCache.php

View workflow job for this annotation

GitHub Actions / Nextcloud

InvalidPropertyAssignmentValue

lib/Share/Helper/FilesMetadataCache.php:84:5: InvalidPropertyAssignmentValue: $this->filesSizeData with declared type 'array<int, array{height: int, width: int}>' cannot be assigned type 'non-empty-array<int, array{height: int, width: int}|null>' (see https://psalm.dev/145)
return;
}

if (isset($sizeMetadata['width'], $sizeMetadata['height'])) {
$dimensions = [
'width' => $sizeMetadata['width'],
'height' => $sizeMetadata['height'],
];
$this->filesSizeData[$fileId] = $dimensions;
} else {
$this->filesSizeData[$fileId] = null;

Check failure on line 95 in lib/Share/Helper/FilesMetadataCache.php

View workflow job for this annotation

GitHub Actions / Nextcloud

InvalidPropertyAssignmentValue

lib/Share/Helper/FilesMetadataCache.php:95:5: InvalidPropertyAssignmentValue: $this->filesSizeData with declared type 'array<int, array{height: int, width: int}>' cannot be assigned type 'non-empty-array<int, array{height: int, width: int}|null>' (see https://psalm.dev/145)
}
} else {
$this->filesSizeData[$fileId] = null;

Check failure on line 98 in lib/Share/Helper/FilesMetadataCache.php

View workflow job for this annotation

GitHub Actions / Nextcloud

InvalidPropertyAssignmentValue

lib/Share/Helper/FilesMetadataCache.php:98:4: InvalidPropertyAssignmentValue: $this->filesSizeData with declared type 'array<int, array{height: int, width: int}>' cannot be assigned type 'non-empty-array<int, array{height: int, width: int}|null>' (see https://psalm.dev/145)
}

}
}

0 comments on commit e2c87ff

Please sign in to comment.