summaryrefslogtreecommitdiffstats
path: root/lib/private/Metadata/Provider/ExifProvider.php
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2022-08-11 19:26:03 +0200
committerLouis Chemineau <louis@chmn.me>2022-10-11 10:41:17 +0200
commit92d065b6ebaa09c316118d9a155d45f69b00f19d (patch)
tree2db0801052e2e3e43a27a198f0714bc60ef8f755 /lib/private/Metadata/Provider/ExifProvider.php
parent5fd3971f35bfd2a7b9e8cf8fd69050035da5e974 (diff)
downloadnextcloud-server-92d065b6ebaa09c316118d9a155d45f69b00f19d.tar.gz
nextcloud-server-92d065b6ebaa09c316118d9a155d45f69b00f19d.zip
Extract GPS data from EXIF
Signed-off-by: Louis Chemineau <louis@chmn.me>
Diffstat (limited to 'lib/private/Metadata/Provider/ExifProvider.php')
-rw-r--r--lib/private/Metadata/Provider/ExifProvider.php86
1 files changed, 72 insertions, 14 deletions
diff --git a/lib/private/Metadata/Provider/ExifProvider.php b/lib/private/Metadata/Provider/ExifProvider.php
index 892671556b3..aa0b5464682 100644
--- a/lib/private/Metadata/Provider/ExifProvider.php
+++ b/lib/private/Metadata/Provider/ExifProvider.php
@@ -5,10 +5,19 @@ namespace OC\Metadata\Provider;
use OC\Metadata\FileMetadata;
use OC\Metadata\IMetadataProvider;
use OCP\Files\File;
+use Psr\Log\LoggerInterface;
class ExifProvider implements IMetadataProvider {
+ private LoggerInterface $logger;
+
+ public function __construct(
+ LoggerInterface $logger
+ ) {
+ $this->logger = $logger;
+ }
+
public static function groupsProvided(): array {
- return ['size'];
+ return ['size', 'gps'];
}
public static function isAvailable(): bool {
@@ -16,8 +25,21 @@ class ExifProvider implements IMetadataProvider {
}
public function execute(File $file): array {
+ $exifData = [];
$fileDescriptor = $file->fopen('rb');
- $data = exif_read_data($fileDescriptor, 'COMPUTED', true);
+
+ $data = null;
+ try {
+ // 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 why 1 as a special meaning.
+ // Revert right after reading the exif data.
+ $oldBufferSize = stream_set_chunk_size($fileDescriptor, 1);
+ $data = exif_read_data($fileDescriptor, 'ANY_TAG', true);
+ stream_set_chunk_size($fileDescriptor, $oldBufferSize);
+ } catch (\Exception $ex) {
+ $this->logger->warning("Couldn't extract metadata for ".$file->getId(), ['exception' => $ex]);
+ }
$size = new FileMetadata();
$size->setGroupName('size');
@@ -31,29 +53,65 @@ class ExifProvider implements IMetadataProvider {
'width' => $sizeResult[0],
'height' => $sizeResult[1],
]);
+
+ $exifData['size'] = $size;
}
+ } elseif (array_key_exists('COMPUTED', $data)) {
+ if (array_key_exists('Width', $data['COMPUTED']) && array_key_exists('Height', $data['COMPUTED'])) {
+ $size->setMetadata([
+ 'width' => $data['COMPUTED']['Width'],
+ 'height' => $data['COMPUTED']['Height'],
+ ]);
- return [
- 'size' => $size,
- ];
+ $exifData['size'] = $size;
+ }
}
- if (array_key_exists('COMPUTED', $data)
- && array_key_exists('Width', $data['COMPUTED'])
- && array_key_exists('Height', $data['COMPUTED'])
+ if ($data && array_key_exists('GPS', $data)
+ && array_key_exists('GPSLatitude', $data['GPS']) && array_key_exists('GPSLatitudeRef', $data['GPS'])
+ && array_key_exists('GPSLongitude', $data['GPS']) && array_key_exists('GPSLongitudeRef', $data['GPS'])
) {
- $size->setMetadata([
- 'width' => $data['COMPUTED']['Width'],
- 'height' => $data['COMPUTED']['Height'],
+ $gps = new FileMetadata();
+ $gps->setGroupName('gps');
+ $gps->setId($file->getId());
+ $gps->setMetadata([
+ 'latitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLatitude'], $data['GPS']['GPSLatitudeRef']),
+ 'longitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLongitude'], $data['GPS']['GPSLongitudeRef']),
]);
+
+ $exifData['gps'] = $gps;
}
- return [
- 'size' => $size,
- ];
+ return $exifData;
}
public static function getMimetypesSupported(): string {
return '/image\/.*/';
}
+
+ /**
+ * @param array|string $coordinates
+ */
+ private static 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(function (string $rawDegree) {
+ $parts = explode('/', $rawDegree);
+
+ if ($parts[1] === '0') {
+ return 0;
+ }
+
+ return floatval($parts[0]) / floatval($parts[1] ?? 1);
+ }, $coordinates);
+
+ $sign = ($hemisphere === 'W' || $hemisphere === 'S') ? -1 : 1;
+ return $sign * ($degrees + $minutes / 60 + $seconds / 3600);
+ }
}