123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- import "dart:io";
- import "package:computer/computer.dart";
- import 'package:exif/exif.dart';
- import 'package:intl/intl.dart';
- import 'package:logging/logging.dart';
- import 'package:photos/models/file/file.dart';
- import "package:photos/models/location/location.dart";
- import "package:photos/services/location_service.dart";
- import 'package:photos/utils/file_util.dart';
- const kDateTimeOriginal = "EXIF DateTimeOriginal";
- const kImageDateTime = "Image DateTime";
- const kExifOffSetKeys = [
- "EXIF OffsetTime",
- "EXIF OffsetTimeOriginal",
- "EXIF OffsetTimeDigitized",
- ];
- const kExifDateTimePattern = "yyyy:MM:dd HH:mm:ss";
- const kEmptyExifDateTime = "0000:00:00 00:00:00";
- final _logger = Logger("ExifUtil");
- Future<Map<String, IfdTag>> getExif(EnteFile file) async {
- try {
- final File? originFile = await getFile(file, isOrigin: true);
- if (originFile == null) {
- throw Exception("Failed to fetch origin file");
- }
- final exif = await readExifAsync(originFile);
- if (!file.isRemoteFile && Platform.isIOS) {
- await originFile.delete();
- }
- return exif;
- } catch (e) {
- _logger.severe("failed to getExif", e);
- rethrow;
- }
- }
- Future<Map<String, IfdTag>?> getExifFromSourceFile(File originFile) async {
- try {
- final exif = await readExifAsync(originFile);
- return exif;
- } catch (e, s) {
- _logger.severe("failed to get exif from origin file", e, s);
- return null;
- }
- }
- Future<DateTime?> getCreationTimeFromEXIF(
- File? file,
- Map<String, IfdTag>? exifData,
- ) async {
- try {
- assert(file != null || exifData != null);
- final exif = exifData ?? await readExifAsync(file!);
- final exifTime = exif.containsKey(kDateTimeOriginal)
- ? exif[kDateTimeOriginal]!.printable
- : exif.containsKey(kImageDateTime)
- ? exif[kImageDateTime]!.printable
- : null;
- if (exifTime != null && exifTime != kEmptyExifDateTime) {
- String? exifOffsetTime;
- for (final key in kExifOffSetKeys) {
- if (exif.containsKey(key)) {
- exifOffsetTime = exif[key]!.printable;
- break;
- }
- }
- return getDateTimeInDeviceTimezone(exifTime, exifOffsetTime);
- }
- } catch (e) {
- _logger.severe("failed to getCreationTimeFromEXIF", e);
- }
- return null;
- }
- DateTime getDateTimeInDeviceTimezone(String exifTime, String? offsetString) {
- final DateTime result = DateFormat(kExifDateTimePattern).parse(exifTime);
- if (offsetString == null) {
- return result;
- }
- try {
- final List<String> splitHHMM = offsetString.split(":");
- // Parse the offset from the photo's time zone
- final int offsetHours = int.parse(splitHHMM[0]);
- final int offsetMinutes =
- int.parse(splitHHMM[1]) * (offsetHours.isNegative ? -1 : 1);
- // Adjust the date for the offset to get the photo's correct UTC time
- final photoUtcDate =
- result.add(Duration(hours: -offsetHours, minutes: -offsetMinutes));
- // Getting the current device's time zone offset from UTC
- final now = DateTime.now();
- final localOffset = now.timeZoneOffset;
- // Adjusting the photo's UTC time to the device's local time
- final deviceLocalTime = photoUtcDate.add(localOffset);
- return deviceLocalTime;
- } catch (e, s) {
- _logger.severe("tz offset adjust failed $offsetString", e, s);
- }
- return result;
- }
- Location? locationFromExif(Map<String, IfdTag> exif) {
- try {
- return gpsDataFromExif(exif).toLocationObj();
- } catch (e, s) {
- _logger.severe("failed to get location from exif", e, s);
- return null;
- }
- }
- Future<Map<String, IfdTag>> _readExifArgs(Map<String, dynamic> args) {
- return readExifFromFile(args["file"]);
- }
- Future<Map<String, IfdTag>> readExifAsync(File file) async {
- return await Computer.shared().compute(
- _readExifArgs,
- param: {"file": file},
- taskName: "readExifAsync",
- );
- }
- GPSData gpsDataFromExif(Map<String, IfdTag> exif) {
- final Map<String, dynamic> exifLocationData = {
- "lat": null,
- "long": null,
- "latRef": null,
- "longRef": null,
- };
- if (exif["GPS GPSLatitude"] != null) {
- exifLocationData["lat"] = exif["GPS GPSLatitude"]!
- .values
- .toList()
- .map((e) => ((e as Ratio).numerator / e.denominator))
- .toList();
- }
- if (exif["GPS GPSLongitude"] != null) {
- exifLocationData["long"] = exif["GPS GPSLongitude"]!
- .values
- .toList()
- .map((e) => ((e as Ratio).numerator / e.denominator))
- .toList();
- }
- if (exif["GPS GPSLatitudeRef"] != null) {
- exifLocationData["latRef"] = exif["GPS GPSLatitudeRef"].toString();
- }
- if (exif["GPS GPSLongitudeRef"] != null) {
- exifLocationData["longRef"] = exif["GPS GPSLongitudeRef"].toString();
- }
- return GPSData(
- exifLocationData["latRef"],
- exifLocationData["lat"],
- exifLocationData["longRef"],
- exifLocationData["long"],
- );
- }
|