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);
}