diff options
author | Louis Chemineau <louis@chmn.me> | 2022-08-11 19:26:03 +0200 |
---|---|---|
committer | Louis Chemineau <louis@chmn.me> | 2022-10-11 10:41:17 +0200 |
commit | 92d065b6ebaa09c316118d9a155d45f69b00f19d (patch) | |
tree | 2db0801052e2e3e43a27a198f0714bc60ef8f755 /lib/private/Metadata/Provider/ExifProvider.php | |
parent | 5fd3971f35bfd2a7b9e8cf8fd69050035da5e974 (diff) | |
download | nextcloud-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.php | 86 |
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); + } } |