diff --git a/appinfo/info.xml b/appinfo/info.xml index cbcefa5bc..677c63729 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -32,7 +32,6 @@ OCA\Photos\Command\UpdateReverseGeocodingFilesCommand - OCA\Photos\Command\MapMediaToPlaceCommand @@ -48,4 +47,4 @@ OCA\Photos\Jobs\AutomaticPlaceMapperJob - + \ No newline at end of file diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index a0f50f199..5c4523b72 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -28,15 +28,19 @@ use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\Events\SabrePluginAuthInitEvent; use OCA\Photos\Listener\AlbumsManagementEventListener; -use OCA\Photos\Listener\PlaceManagerEventListener; use OCA\Photos\Listener\SabrePluginAuthInitListener; use OCA\Photos\Listener\TagListener; +use OCA\Photos\MetadataProvider\ExifMetadataProvider; +use OCA\Photos\MetadataProvider\OriginalDateTimeMetadataProvider; +use OCA\Photos\MetadataProvider\PlaceMetadataProvider; +use OCA\Photos\MetadataProvider\SizeMetadataProvider; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\Files\Events\Node\NodeDeletedEvent; -use OCP\Files\Events\Node\NodeWrittenEvent; +use OCP\FilesMetadata\Event\MetadataBackgroundEvent; +use OCP\FilesMetadata\Event\MetadataLiveEvent; use OCP\Group\Events\GroupDeletedEvent; use OCP\Group\Events\UserRemovedEvent; use OCP\Share\Events\ShareDeletedEvent; @@ -75,8 +79,12 @@ public function register(IRegistrationContext $context): void { /** Register $principalBackend for the DAV collection */ $context->registerServiceAlias('principalBackend', Principal::class); - // Priority of -1 to be triggered after event listeners populating metadata. - $context->registerEventListener(NodeWrittenEvent::class, PlaceManagerEventListener::class, -1); + // Metadata + $context->registerEventListener(MetadataLiveEvent::class, ExifMetadataProvider::class); + $context->registerEventListener(MetadataLiveEvent::class, SizeMetadataProvider::class); + $context->registerEventListener(MetadataLiveEvent::class, OriginalDateTimeMetadataProvider::class); + $context->registerEventListener(MetadataLiveEvent::class, PlaceMetadataProvider::class); + $context->registerEventListener(MetadataBackgroundEvent::class, PlaceMetadataProvider::class); $context->registerEventListener(NodeDeletedEvent::class, AlbumsManagementEventListener::class); $context->registerEventListener(UserRemovedEvent::class, AlbumsManagementEventListener::class); diff --git a/lib/Command/MapMediaToPlaceCommand.php b/lib/Command/MapMediaToPlaceCommand.php deleted file mode 100644 index 2c95d8bc8..000000000 --- a/lib/Command/MapMediaToPlaceCommand.php +++ /dev/null @@ -1,116 +0,0 @@ - - * - * @author Louis Chemineau - * - * @license AGPL-3.0-or-later - * - * 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 . - * - */ -namespace OCA\Photos\Command; - -use OCA\Photos\Service\MediaPlaceManager; -use OCP\Files\Folder; -use OCP\Files\IRootFolder; -use OCP\IConfig; -use OCP\IUserManager; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -class MapMediaToPlaceCommand extends Command { - public function __construct( - private IRootFolder $rootFolder, - private MediaPlaceManager $mediaPlaceManager, - private IConfig $config, - private IUserManager $userManager, - ) { - parent::__construct(); - } - - /** - * Configure the command - */ - protected function configure(): void { - $this->setName('photos:map-media-to-place') - ->setDescription('Reverse geocode media coordinates.') - ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Limit the mapping to a user.', null); - } - - /** - * Execute the command - */ - protected function execute(InputInterface $input, OutputInterface $output): int { - if (!$this->config->getSystemValueBool('enable_file_metadata', true)) { - throw new \Exception('File metadata is not enabled.'); - } - - $userId = $input->getOption('user'); - if ($userId === null) { - $this->scanForAllUsers($output); - } else { - $this->scanFilesForUser($userId, $output); - } - - return 0; - } - - private function scanForAllUsers(OutputInterface $output): void { - $users = $this->userManager->search(''); - - $output->writeln("Scanning all users:"); - foreach ($users as $user) { - $this->scanFilesForUser($user->getUID(), $output); - } - } - - private function scanFilesForUser(string $userId, OutputInterface $output): void { - $userFolder = $this->rootFolder->getUserFolder($userId); - $output->write(" - Scanning files for $userId"); - $startTime = time(); - $count = $this->scanFolder($userFolder); - $timeElapse = time() - $startTime; - $output->writeln(" - $count files, $timeElapse sec"); - } - - private function scanFolder(Folder $folder): int { - $count = 0; - - // Do not scan share and other moveable mounts. - if ($folder->getMountPoint() instanceof \OC\Files\Mount\MoveableMount) { - return $count; - } - - foreach ($folder->getDirectoryListing() as $node) { - if ($node instanceof Folder) { - $count += $this->scanFolder($node); - continue; - } - - if (!str_starts_with($node->getMimeType(), 'image')) { - continue; - } - - $this->mediaPlaceManager->setPlaceForFile($node->getId()); - $count++; - } - - return $count; - } -} diff --git a/lib/DB/Place/PlaceMapper.php b/lib/DB/Place/PlaceMapper.php index 7147dc8d7..9791f29bd 100644 --- a/lib/DB/Place/PlaceMapper.php +++ b/lib/DB/Place/PlaceMapper.php @@ -25,11 +25,11 @@ namespace OCA\Photos\DB\Place; -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\IMimeTypeLoader; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\IDBConnection; class PlaceMapper { @@ -39,6 +39,7 @@ public function __construct( private IDBConnection $connection, private IMimeTypeLoader $mimeTypeLoader, private IRootFolder $rootFolder, + private IFilesMetadataManager $filesMetadataManager, ) { } @@ -166,30 +167,7 @@ public function findFileForUserAndPlace(string $userId, string $place, string $f } public function setPlaceForFile(string $place, int $fileId): void { - try { - $query = $this->connection->getQueryBuilder(); - $query->insert('file_metadata') - ->values([ - "id" => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT), - "group_name" => $query->createNamedParameter(self::METADATA_GROUP), - "value" => $query->createNamedParameter($place), - ]) - ->executeStatement(); - } catch (\Exception $ex) { - if ($ex->getPrevious() instanceof UniqueConstraintViolationException) { - $this->updatePlaceForFile($place, $fileId); - } else { - throw $ex; - } - } - } - - public function updatePlaceForFile(string $place, int $fileId): void { - $query = $this->connection->getQueryBuilder(); - $query->update('file_metadata') - ->set("value", $query->createNamedParameter($place)) - ->where($query->expr()->eq('id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) - ->andWhere($query->expr()->eq('group_name', $query->createNamedParameter(self::METADATA_GROUP))) - ->executeStatement(); + $metadata = $this->filesMetadataManager->getMetadata($fileId, true); + $metadata->set('gps', $place, true); } } diff --git a/lib/Jobs/MapMediaToPlaceJob.php b/lib/Jobs/MapMediaToPlaceJob.php deleted file mode 100644 index 2facda95a..000000000 --- a/lib/Jobs/MapMediaToPlaceJob.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * @author Louis Chemineau - * - * @license AGPL-3.0-or-later - * - * 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 . - * - */ - -namespace OCA\Photos\Jobs; - -use OCA\Photos\Service\MediaPlaceManager; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\BackgroundJob\QueuedJob; - -class MapMediaToPlaceJob extends QueuedJob { - private MediaPlaceManager $mediaPlaceManager; - - public function __construct( - ITimeFactory $time, - MediaPlaceManager $mediaPlaceManager - ) { - parent::__construct($time); - $this->mediaPlaceManager = $mediaPlaceManager; - } - - protected function run($argument) { - [$fileId] = $argument; - - $this->mediaPlaceManager->setPlaceForFile($fileId); - } -} diff --git a/lib/Listener/PlaceManagerEventListener.php b/lib/Listener/PlaceManagerEventListener.php deleted file mode 100644 index c9954f853..000000000 --- a/lib/Listener/PlaceManagerEventListener.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * @author Louis Chemineau - * - * @license AGPL-3.0-or-later - * - * 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 . - * - */ - -namespace OCA\Photos\Listener; - -use OCA\Photos\Jobs\MapMediaToPlaceJob; -use OCA\Photos\Service\MediaPlaceManager; -use OCP\BackgroundJob\IJobList; -use OCP\EventDispatcher\Event; -use OCP\EventDispatcher\IEventListener; -use OCP\Files\Events\Node\NodeWrittenEvent; -use OCP\IConfig; - -/** - * Listener to add place info from the database. - */ -class PlaceManagerEventListener implements IEventListener { - public function __construct( - private MediaPlaceManager $mediaPlaceManager, - private IConfig $config, - private IJobList $jobList, - ) { - } - - public function handle(Event $event): void { - if (!$this->config->getSystemValueBool('enable_file_metadata', true)) { - return; - } - - if ($event instanceof NodeWrittenEvent) { - if (!$this->isCorrectPath($event->getNode()->getPath())) { - return; - } - - if (!str_starts_with($event->getNode()->getMimeType(), 'image')) { - return; - } - - $fileId = $event->getNode()->getId(); - - $this->jobList->add(MapMediaToPlaceJob::class, [$fileId]); - } - } - - private function isCorrectPath(string $path): bool { - // TODO make this more dynamic, we have the same issue in other places - return !str_starts_with($path, 'appdata_') && !str_starts_with($path, 'files_versions/'); - } -} diff --git a/lib/MetadataProvider/ExifMetadataProvider.php b/lib/MetadataProvider/ExifMetadataProvider.php new file mode 100644 index 000000000..167b98b1e --- /dev/null +++ b/lib/MetadataProvider/ExifMetadataProvider.php @@ -0,0 +1,136 @@ + + * @copyright Copyright 2022 Louis Chmn + * @license AGPL-3.0-or-later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Photos\MetadataProvider; + +use OCA\Photos\AppInfo\Application; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\File; +use OCP\FilesMetadata\Event\MetadataLiveEvent; +use Psr\Log\LoggerInterface; + +/** + * Extract EXIF, IFD0, and GPS data from a picture file. + * EXIF data reference: https://web.archive.org/web/20220428165430/exif.org/Exif2-2.PDF + * + * @template-implements IEventListener + */ +class ExifMetadataProvider implements IEventListener { + public function __construct( + private LoggerInterface $logger + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof MetadataLiveEvent)) { + return; + } + + $node = $event->getNode(); + + if (!$node instanceof File) { + return; + } + + $path = $node->getPath(); + if (str_starts_with($path, 'appdata_') || str_starts_with($path, 'files_versions/') || str_starts_with($path, 'files_trashbin/')) { + return; + } + + if (!in_array($node->getMimeType(), Application::IMAGE_MIMES)) { + return; + } + + if (!extension_loaded('exif')) { + return; + } + + $fileDescriptor = $node->fopen('rb'); + if ($fileDescriptor === false) { + return; + } + + $rawExifData = null; + + try { + // HACK: The stream_set_chunk_size call is needed to make reading exif data reliable. + // This is to trigger this condition: https://github.com/php/php-src/blob/d64aa6f646a7b5e58359dc79479860164239580a/main/streams/streams.c#L710 + // But I don't understand yet why 1 as a special meaning. + $oldBufferSize = stream_set_chunk_size($fileDescriptor, 1); + $rawExifData = @exif_read_data($fileDescriptor, 'EXIF, GPS', true); + // We then revert the change after having read the exif data. + stream_set_chunk_size($fileDescriptor, $oldBufferSize); + } catch (\Exception $ex) { + $this->logger->info("Failed to extract metadata for " . $node->getId(), ['exception' => $ex]); + } + + if ($rawExifData && array_key_exists('EXIF', $rawExifData)) { + $event->getMetadata()->setArray('photos-exif', $rawExifData['EXIF']); + } + + if ($rawExifData && array_key_exists('IFD0', $rawExifData)) { + $event->getMetadata()->setArray('photos-ifd0', $rawExifData['IFD0']); + } + + if ( + $rawExifData && + array_key_exists('GPS', $rawExifData) && + array_key_exists('GPSLatitude', $rawExifData['GPS']) && array_key_exists('GPSLatitudeRef', $rawExifData['GPS']) && + array_key_exists('GPSLongitude', $rawExifData['GPS']) && array_key_exists('GPSLongitudeRef', $rawExifData['GPS']) + ) { + $event->getMetadata()->setArray('photos-gps', [ + 'latitude' => $this->gpsDegreesToDecimal($rawExifData['GPS']['GPSLatitude'], $rawExifData['GPS']['GPSLatitudeRef']), + 'longitude' => $this->gpsDegreesToDecimal($rawExifData['GPS']['GPSLongitude'], $rawExifData['GPS']['GPSLongitudeRef']), + 'altitude' => ($rawExifData['GPS']['GPSAltitudeRef'] === "\u{0000}" ? 1 : -1) * $this->parseGPSData($rawExifData['GPS']['GPSAltitude']), + ]); + } + } + + /** + * @param array|string $coordinates + */ + private function gpsDegreesToDecimal($coordinates, ?string $hemisphere): float { + if (is_string($coordinates)) { + $coordinates = array_map("trim", explode(",", $coordinates)); + } + + if (count($coordinates) !== 3) { + throw new \Exception('Invalid coordinate format: ' . json_encode($coordinates)); + } + + [$degrees, $minutes, $seconds] = array_map(fn ($rawDegree) => $this->parseGPSData($rawDegree), $coordinates); + + $sign = ($hemisphere === 'W' || $hemisphere === 'S') ? -1 : 1; + return $sign * ($degrees + $minutes / 60 + $seconds / 3600); + } + + private function parseGPSData(string $rawData): float { + $parts = explode('/', $rawData); + + if ($parts[1] === '0') { + return 0; + } + + return floatval($parts[0]) / floatval($parts[1] ?? 1); + } +} diff --git a/lib/MetadataProvider/OriginalDateTimeMetadataProvider.php b/lib/MetadataProvider/OriginalDateTimeMetadataProvider.php new file mode 100644 index 000000000..6db62a8a1 --- /dev/null +++ b/lib/MetadataProvider/OriginalDateTimeMetadataProvider.php @@ -0,0 +1,94 @@ + + * @copyright Copyright 2022 Louis Chmn + * @license AGPL-3.0-or-later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Photos\MetadataProvider; + +use DateTime; +use OCA\Photos\AppInfo\Application; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\File; +use OCP\FilesMetadata\Event\MetadataLiveEvent; + +/** + * @template-implements IEventListener + */ +class OriginalDateTimeMetadataProvider implements IEventListener { + public function __construct() { + } + + public array $regexpToDateFormatMap = [ + "/^IMG_([0-9]{8}_[0-9]{6})/" => "Ymd_Gis", + "/^PANO_([0-9]{8}_[0-9]{6})/" => "Ymd_Gis", + "/^PXL_([0-9]{8}_[0-9]{6})/" => "Ymd_Gis", + "/^([0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{4})/" => "Y-m-d-G-i-s", + ]; + + public function handle(Event $event): void { + if (!($event instanceof MetadataLiveEvent)) { + return; + } + + $node = $event->getNode(); + + if (!$node instanceof File) { + return; + } + + $path = $node->getPath(); + if (str_starts_with($path, 'appdata_') || str_starts_with($path, 'files_versions/') || str_starts_with($path, 'files_trashbin/')) { + return; + } + + if (!in_array($node->getMimeType(), Application::IMAGE_MIMES)) { + return; + } + + $metadata = $event->getMetadata(); + + // Try to use EXIF data. + if ($metadata->hasKey('photos-exif') && array_key_exists('DateTimeOriginal', $metadata->getArray('photos-exif'))) { + $rawDateTimeOriginal = $metadata->getArray('photos-exif')['DateTimeOriginal']; + $dateTimeOriginal = DateTime::createFromFormat("Y:m:d G:i:s", $rawDateTimeOriginal); + $metadata->setInt('photos-original_date_time', $dateTimeOriginal->getTimestamp(), true); + return; + } + + // Try to parse the date out of the name. + $name = $node->getName(); + $matches = []; + + foreach ($this->regexpToDateFormatMap as $regexp => $format) { + $matchesCount = preg_match($regexp, $name, $matches); + if ($matchesCount === 0) { + continue; + } + + $dateTimeOriginal = DateTime::createFromFormat($format, $matches[1]); + $metadata->setInt('photos-original_date_time', $dateTimeOriginal->getTimestamp(), true); + return; + } + + // Fallback to the mtime. + $metadata->setInt('photos-original_date_time', $node->getMTime(), true); + } +} diff --git a/lib/MetadataProvider/PlaceMetadataProvider.php b/lib/MetadataProvider/PlaceMetadataProvider.php new file mode 100644 index 000000000..252bf9228 --- /dev/null +++ b/lib/MetadataProvider/PlaceMetadataProvider.php @@ -0,0 +1,65 @@ + + * @license AGPL-3.0-or-later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + + +namespace OCA\Photos\MetadataProvider; + +use OCA\Photos\AppInfo\Application; +use OCA\Photos\Service\MediaPlaceManager; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\File; +use OCP\FilesMetadata\Event\MetadataBackgroundEvent; +use OCP\FilesMetadata\Event\MetadataLiveEvent; + +class PlaceMetadataProvider implements IEventListener { + public function __construct( + private MediaPlaceManager $mediaPlaceManager + ) { + } + + public function handle(Event $event): void { + if ($event instanceof MetadataLiveEvent) { + $node = $event->getNode(); + + if (!$node instanceof File) { + return; + } + + $path = $node->getPath(); + if (str_starts_with($path, 'appdata_') || str_starts_with($path, 'files_versions/') || str_starts_with($path, 'files_trashbin/')) { + return; + } + + if (!in_array($node->getMimeType(), Application::IMAGE_MIMES)) { + return; + } + + $event->requestBackgroundJob(); + } + + if ($event instanceof MetadataBackgroundEvent) { + $metadata = $event->getMetadata(); + $place = $this->mediaPlaceManager->getPlaceForFile($event->getNode()->getId()); + $metadata->set('photos-place', $place, true); + } + } +} diff --git a/lib/MetadataProvider/SizeMetadataProvider.php b/lib/MetadataProvider/SizeMetadataProvider.php new file mode 100644 index 000000000..e262a4436 --- /dev/null +++ b/lib/MetadataProvider/SizeMetadataProvider.php @@ -0,0 +1,72 @@ + + * @copyright Copyright 2022 Louis Chmn + * @license AGPL-3.0-or-later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Photos\MetadataProvider; + +use OCA\Photos\AppInfo\Application; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\File; +use OCP\FilesMetadata\Event\MetadataLiveEvent; +use Psr\Log\LoggerInterface; + +/** + * @template-implements IEventListener + */ +class SizeMetadataProvider implements IEventListener { + public function __construct( + private LoggerInterface $logger + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof MetadataLiveEvent)) { + return; + } + + $node = $event->getNode(); + + if (!$node instanceof File) { + return; + } + + $path = $node->getPath(); + if (str_starts_with($path, 'appdata_') || str_starts_with($path, 'files_versions/') || str_starts_with($path, 'files_trashbin/')) { + return; + } + + if (!in_array($node->getMimeType(), Application::IMAGE_MIMES)) { + return; + } + + $size = getimagesizefromstring($node->getContent()); + + if ($size === false) { + return; + } + + $event->getMetadata()->setArray('photos-size', [ + 'width' => $size[0], + 'height' => $size[1], + ]); + } +} diff --git a/lib/Service/MediaPlaceManager.php b/lib/Service/MediaPlaceManager.php index c1e11f7b6..24943d183 100644 --- a/lib/Service/MediaPlaceManager.php +++ b/lib/Service/MediaPlaceManager.php @@ -25,12 +25,12 @@ namespace OCA\Photos\Service; -use OC\Metadata\IMetadataManager; use OCA\Photos\DB\Place\PlaceMapper; +use OCP\FilesMetadata\IFilesMetadataManager; class MediaPlaceManager { public function __construct( - private IMetadataManager $metadataManager, + private IFilesMetadataManager $filesMetadataManager, private ReverseGeoCoderService $rgcService, private PlaceMapper $placeMapper, ) { @@ -46,30 +46,27 @@ public function setPlaceForFile(int $fileId): void { $this->placeMapper->setPlaceForFile($place, $fileId); } - public function updatePlaceForFile(int $fileId): void { - $place = $this->getPlaceForFile($fileId); + // public function updatePlaceForFile(int $fileId): void { + // $place = $this->getPlaceForFile($fileId); - if ($place === null) { - return; - } + // if ($place === null) { + // return; + // } - $this->placeMapper->updatePlaceForFile($place, $fileId); - } + // $this->placeMapper->setPlaceForFile($place, $fileId); + // } - private function getPlaceForFile(int $fileId): ?string { - $gpsMetadata = $this->metadataManager->fetchMetadataFor('gps', [$fileId])[$fileId]; - $metadata = $gpsMetadata->getDecodedValue(); + public function getPlaceForFile(int $fileId): ?string { + $metadata = $this->filesMetadataManager->getMetadata($fileId, true); - if (count($metadata) === 0) { + if (!$metadata->hasKey('photos-gps')) { return null; } - $latitude = $metadata['latitude']; - $longitude = $metadata['longitude']; + $coordinate = $metadata->getArray('photos-gps'); - if ($latitude === null || $longitude === null) { - return null; - } + $latitude = $coordinate['latitude']; + $longitude = $coordinate['longitude']; return $this->rgcService->getPlaceForCoordinates($latitude, $longitude); }