From 8e1aeaa0131f5cb85fc922a501a479d74e6990f5 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Sat, 27 Aug 2022 23:32:31 +0800 Subject: [PATCH] Run reverse geocoding in metadata task --- app/lib/entity/file_util.dart | 5 +- app/lib/metadata_task_manager.dart | 11 ++- app/lib/service.dart | 7 +- app/lib/use_case/update_missing_metadata.dart | 78 ++++++++++++++----- 4 files changed, 73 insertions(+), 28 deletions(-) diff --git a/app/lib/entity/file_util.dart b/app/lib/entity/file_util.dart index 1c093755..c0d51459 100644 --- a/app/lib/entity/file_util.dart +++ b/app/lib/entity/file_util.dart @@ -88,8 +88,11 @@ bool isNoMediaMarkerPath(String path) { } /// Return if there's missing metadata in [file] +/// +/// Current this function will check both [File.metadata] and [File.location] bool isMissingMetadata(File file) => - isSupportedImageFormat(file) && file.metadata == null; + isSupportedImageFormat(file) && + (file.metadata == null || file.location == null); final _supportedFormatMimes = [ "image/jpeg", diff --git a/app/lib/metadata_task_manager.dart b/app/lib/metadata_task_manager.dart index f7a9bbf7..aa404195 100644 --- a/app/lib/metadata_task_manager.dart +++ b/app/lib/metadata_task_manager.dart @@ -9,6 +9,7 @@ import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/entity/file_util.dart' as file_util; import 'package:nc_photos/event/event.dart'; import 'package:nc_photos/pref.dart'; +import 'package:nc_photos/reverse_geocoder.dart'; import 'package:nc_photos/use_case/update_missing_metadata.dart'; /// Task to update metadata for missing files @@ -29,11 +30,13 @@ class MetadataTask { final shareFolder = File(path: file_util.unstripPath(account, pref.getShareFolderOr())); bool hasScanShareFolder = false; + final geocoder = ReverseGeocoder(); + await geocoder.init(); for (final r in account.roots) { final dir = File(path: file_util.unstripPath(account, r)); hasScanShareFolder |= file_util.isOrUnderDir(shareFolder, dir); - final op = UpdateMissingMetadata( - _c.fileRepo, const _UpdateMissingMetadataConfigProvider()); + final op = UpdateMissingMetadata(_c.fileRepo, + const _UpdateMissingMetadataConfigProvider(), geocoder); await for (final _ in op(account, dir)) { if (!Pref().isEnableExifOr()) { _log.info("[call] EXIF disabled, task ending immaturely"); @@ -43,8 +46,8 @@ class MetadataTask { } } if (!hasScanShareFolder) { - final op = UpdateMissingMetadata( - _c.fileRepo, const _UpdateMissingMetadataConfigProvider()); + final op = UpdateMissingMetadata(_c.fileRepo, + const _UpdateMissingMetadataConfigProvider(), geocoder); await for (final _ in op( account, shareFolder, diff --git a/app/lib/service.dart b/app/lib/service.dart index de0c6dff..6c17358f 100644 --- a/app/lib/service.dart +++ b/app/lib/service.dart @@ -19,6 +19,7 @@ import 'package:nc_photos/event/native_event.dart'; import 'package:nc_photos/future_extension.dart'; import 'package:nc_photos/language_util.dart' as language_util; import 'package:nc_photos/pref.dart'; +import 'package:nc_photos/reverse_geocoder.dart'; import 'package:nc_photos/use_case/update_missing_metadata.dart'; import 'package:nc_photos_plugin/nc_photos_plugin.dart'; @@ -248,11 +249,13 @@ class _MetadataTask { path: file_util.unstripPath(account, accountPref.getShareFolderOr())); bool hasScanShareFolder = false; final c = KiwiContainer().resolve(); + final geocoder = ReverseGeocoder(); + await geocoder.init(); for (final r in account.roots) { final dir = File(path: file_util.unstripPath(account, r)); hasScanShareFolder |= file_util.isOrUnderDir(shareFolder, dir); final updater = UpdateMissingMetadata( - c.fileRepo, const _UpdateMissingMetadataConfigProvider()); + c.fileRepo, const _UpdateMissingMetadataConfigProvider(), geocoder); void onServiceStop() { _log.info("[_updateMetadata] Stopping task: user canceled"); updater.stop(); @@ -275,7 +278,7 @@ class _MetadataTask { } if (!hasScanShareFolder) { final shareUpdater = UpdateMissingMetadata( - c.fileRepo, const _UpdateMissingMetadataConfigProvider()); + c.fileRepo, const _UpdateMissingMetadataConfigProvider(), geocoder); void onServiceStop() { _log.info("[_updateMetadata] Stopping task: user canceled"); shareUpdater.stop(); diff --git a/app/lib/use_case/update_missing_metadata.dart b/app/lib/use_case/update_missing_metadata.dart index 4e87dfd7..8f11d09b 100644 --- a/app/lib/use_case/update_missing_metadata.dart +++ b/app/lib/use_case/update_missing_metadata.dart @@ -4,11 +4,13 @@ import 'package:kiwi/kiwi.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/connectivity_util.dart' as connectivity_util; +import 'package:nc_photos/entity/exif_extension.dart'; import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/event/event.dart'; import 'package:nc_photos/exception.dart'; import 'package:nc_photos/exception_event.dart'; import 'package:nc_photos/or_null.dart'; +import 'package:nc_photos/reverse_geocoder.dart'; import 'package:nc_photos/use_case/get_file_binary.dart'; import 'package:nc_photos/use_case/load_metadata.dart'; import 'package:nc_photos/use_case/scan_missing_metadata.dart'; @@ -19,7 +21,7 @@ abstract class UpdateMissingMetadataConfigProvider { } class UpdateMissingMetadata { - UpdateMissingMetadata(this.fileRepo, this.configProvider); + UpdateMissingMetadata(this.fileRepo, this.configProvider, this.geocoder); /// Update metadata for all files that support one under a dir /// @@ -43,6 +45,9 @@ class UpdateMissingMetadata { isRecursive: isRecursive, ); await for (final d in dataStream) { + if (!_shouldRun) { + return; + } if (d is ExceptionEvent) { yield d; continue; @@ -54,28 +59,58 @@ class UpdateMissingMetadata { continue; } try { - // since we need to download multiple images in their original size, - // we only do it with WiFi - await _ensureWifi(); - await _ensureBattery(); - KiwiContainer().resolve().fire( - const MetadataTaskStateChangedEvent(MetadataTaskState.prcoessing)); - if (!_shouldRun) { - return; + OrNull? metadataUpdate; + OrNull? locationUpdate; + if (file.metadata == null) { + // since we need to download multiple images in their original size, + // we only do it with WiFi + await _ensureWifi(); + await _ensureBattery(); + KiwiContainer().resolve().fire( + const MetadataTaskStateChangedEvent( + MetadataTaskState.prcoessing)); + if (!_shouldRun) { + return; + } + _log.fine("[call] Updating metadata for ${file.path}"); + final binary = await GetFileBinary(fileRepo)(account, file); + final metadata = + (await LoadMetadata().loadRemote(account, file, binary)).copyWith( + fileEtag: file.etag, + ); + metadataUpdate = OrNull(metadata); + } else { + _log.finer("[call] Skip updating metadata for ${file.path}"); } - _log.fine("[call] Updating metadata for ${file.path}"); - final binary = await GetFileBinary(fileRepo)(account, file); - final metadata = - (await LoadMetadata().loadRemote(account, file, binary)).copyWith( - fileEtag: file.etag, - ); - await UpdateProperty(fileRepo)( - account, - file, - metadata: OrNull(metadata), - ); - yield file; + final lat = + (metadataUpdate?.obj ?? file.metadata)?.exif?.gpsLatitudeDeg; + final lng = + (metadataUpdate?.obj ?? file.metadata)?.exif?.gpsLongitudeDeg; + try { + ImageLocation? location; + if (lat != null && lng != null) { + _log.fine("[call] Reverse geocoding for ${file.path}"); + final l = await geocoder(lat, lng); + if (l != null) { + location = l.toImageLocation(); + } + } + locationUpdate = OrNull(location ?? ImageLocation.empty()); + } catch (e, stackTrace) { + _log.severe("[call] Failed while reverse geocoding: ${file.path}", e, + stackTrace); + } + + if (metadataUpdate != null || locationUpdate != null) { + await UpdateProperty(fileRepo)( + account, + file, + metadata: metadataUpdate, + location: locationUpdate, + ); + yield file; + } // slow down a bit to give some space for the main isolate await Future.delayed(const Duration(milliseconds: 10)); @@ -123,6 +158,7 @@ class UpdateMissingMetadata { final FileRepo fileRepo; final UpdateMissingMetadataConfigProvider configProvider; + final ReverseGeocoder geocoder; bool _shouldRun = true;