fixed parsing & db save on startup
This commit is contained in:
parent
e4da59a624
commit
27bf3f4dad
5 changed files with 87 additions and 61 deletions
|
@ -147,44 +147,47 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
|||
|
||||
private void getCatalogMetadata(MethodCall call, MethodChannel.Result result) {
|
||||
String path = call.argument("path");
|
||||
String mimeType = call.argument("mimeType");
|
||||
try (InputStream is = new FileInputStream(path)) {
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(is);
|
||||
Map<String, Object> metadataMap = new HashMap<>();
|
||||
if (!Constants.MIME_MP2TS.equalsIgnoreCase(mimeType)) {
|
||||
Metadata metadata = ImageMetadataReader.readMetadata(is);
|
||||
|
||||
// EXIF Sub-IFD
|
||||
ExifSubIFDDirectory exifSubDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
|
||||
if (exifSubDir != null) {
|
||||
if (exifSubDir.containsTag(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)) {
|
||||
metadataMap.put("dateMillis", exifSubDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, null, TimeZone.getDefault()).getTime());
|
||||
}
|
||||
}
|
||||
|
||||
// GPS
|
||||
GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
|
||||
if (gpsDir != null) {
|
||||
GeoLocation geoLocation = gpsDir.getGeoLocation();
|
||||
if (geoLocation != null) {
|
||||
metadataMap.put("latitude", geoLocation.getLatitude());
|
||||
metadataMap.put("longitude", geoLocation.getLongitude());
|
||||
}
|
||||
}
|
||||
|
||||
// XMP
|
||||
XmpDirectory xmpDir = metadata.getFirstDirectoryOfType(XmpDirectory.class);
|
||||
if (xmpDir != null) {
|
||||
XMPMeta xmpMeta = xmpDir.getXMPMeta();
|
||||
try {
|
||||
if (xmpMeta.doesPropertyExist(XMP_DC_SCHEMA_NS, XMP_SUBJECT_PROP_NAME)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int count = xmpMeta.countArrayItems(XMP_DC_SCHEMA_NS, XMP_SUBJECT_PROP_NAME);
|
||||
for (int i = 1; i < count + 1; i++) {
|
||||
XMPProperty item = xmpMeta.getArrayItem(XMP_DC_SCHEMA_NS, XMP_SUBJECT_PROP_NAME, i);
|
||||
sb.append(";").append(item.getValue());
|
||||
}
|
||||
metadataMap.put("xmpSubjects", sb.toString());
|
||||
// EXIF Sub-IFD
|
||||
ExifSubIFDDirectory exifSubDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
|
||||
if (exifSubDir != null) {
|
||||
if (exifSubDir.containsTag(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)) {
|
||||
metadataMap.put("dateMillis", exifSubDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, null, TimeZone.getDefault()).getTime());
|
||||
}
|
||||
}
|
||||
|
||||
// GPS
|
||||
GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
|
||||
if (gpsDir != null) {
|
||||
GeoLocation geoLocation = gpsDir.getGeoLocation();
|
||||
if (geoLocation != null) {
|
||||
metadataMap.put("latitude", geoLocation.getLatitude());
|
||||
metadataMap.put("longitude", geoLocation.getLongitude());
|
||||
}
|
||||
}
|
||||
|
||||
// XMP
|
||||
XmpDirectory xmpDir = metadata.getFirstDirectoryOfType(XmpDirectory.class);
|
||||
if (xmpDir != null) {
|
||||
XMPMeta xmpMeta = xmpDir.getXMPMeta();
|
||||
try {
|
||||
if (xmpMeta.doesPropertyExist(XMP_DC_SCHEMA_NS, XMP_SUBJECT_PROP_NAME)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int count = xmpMeta.countArrayItems(XMP_DC_SCHEMA_NS, XMP_SUBJECT_PROP_NAME);
|
||||
for (int i = 1; i < count + 1; i++) {
|
||||
XMPProperty item = xmpMeta.getArrayItem(XMP_DC_SCHEMA_NS, XMP_SUBJECT_PROP_NAME, i);
|
||||
sb.append(";").append(item.getValue());
|
||||
}
|
||||
metadataMap.put("xmpSubjects", sb.toString());
|
||||
}
|
||||
} catch (XMPException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (XMPException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ public class Constants {
|
|||
|
||||
public static final String MIME_VIDEO = "video";
|
||||
public static final String MIME_GIF = "image/gif";
|
||||
public static final String MIME_MP2TS = "video/mp2ts";
|
||||
|
||||
// video metadata keys, from android.media.MediaMetadataRetriever
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:aves/model/image_decode_service.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/model/image_metadata.dart';
|
||||
import 'package:aves/model/metadata_db.dart';
|
||||
import 'package:aves/widgets/album/all_collection_page.dart';
|
||||
import 'package:aves/widgets/common/fake_app_bar.dart';
|
||||
|
@ -47,10 +48,11 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
eventChannel.receiveBroadcastStream().cast<Map>().listen(
|
||||
(entryMap) => setState(() => entries.add(ImageEntry.fromMap(entryMap))),
|
||||
onDone: () {
|
||||
onDone: () async {
|
||||
debugPrint('mediastore stream done');
|
||||
setState(() {});
|
||||
catalogEntries();
|
||||
await catalogEntries();
|
||||
await locateEntries();
|
||||
},
|
||||
onError: (error) => debugPrint('mediastore stream error=$error'),
|
||||
);
|
||||
|
@ -68,20 +70,36 @@ class _HomePageState extends State<HomePage> {
|
|||
}
|
||||
|
||||
catalogEntries() async {
|
||||
debugPrint('$runtimeType catalogEntries cataloging start');
|
||||
await Future.forEach<ImageEntry>(entries, (entry) async {
|
||||
debugPrint('$runtimeType catalogEntries start');
|
||||
final start = DateTime.now();
|
||||
final uncataloguedEntries = entries.where((entry) => !entry.isCataloged);
|
||||
final newMetadata = List<CatalogMetadata>();
|
||||
await Future.forEach<ImageEntry>(uncataloguedEntries, (entry) async {
|
||||
await entry.catalog();
|
||||
newMetadata.add(entry.catalogMetadata);
|
||||
});
|
||||
debugPrint('$runtimeType catalogEntries cataloging complete');
|
||||
debugPrint('$runtimeType catalogEntries complete in ${DateTime.now().difference(start).inSeconds}s with ${newMetadata.length} new entries');
|
||||
|
||||
// sort with more accurate date
|
||||
entries.sort((a, b) => b.bestDate.compareTo(a.bestDate));
|
||||
setState(() {});
|
||||
|
||||
debugPrint('$runtimeType catalogEntries locating start');
|
||||
await Future.forEach<ImageEntry>(entries, (entry) async {
|
||||
metadataDb.saveMetadata(List.unmodifiable(newMetadata));
|
||||
}
|
||||
|
||||
locateEntries() async {
|
||||
debugPrint('$runtimeType locateEntries start');
|
||||
final start = DateTime.now();
|
||||
final unlocatedEntries = entries.where((entry) => !entry.isLocated);
|
||||
final newAddresses = List<AddressDetails>();
|
||||
await Future.forEach<ImageEntry>(unlocatedEntries, (entry) async {
|
||||
await entry.locate();
|
||||
newAddresses.add(entry.addressDetails);
|
||||
if (newAddresses.length >= 50) {
|
||||
metadataDb.saveAddresses(List.unmodifiable(newAddresses));
|
||||
newAddresses.clear();
|
||||
}
|
||||
});
|
||||
debugPrint('$runtimeType catalogEntries locating done');
|
||||
debugPrint('$runtimeType locateEntries complete in ${DateTime.now().difference(start).inSeconds}s with ${newAddresses.length} new addresses');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,14 +51,18 @@ class MetadataDb {
|
|||
return null;
|
||||
}
|
||||
|
||||
insertMetadata(CatalogMetadata metadata) async {
|
||||
// debugPrint('$runtimeType insertMetadata metadata=$metadata');
|
||||
saveMetadata(Iterable<CatalogMetadata> metadataEntries) async {
|
||||
if (metadataEntries == null || metadataEntries.isEmpty) return;
|
||||
final start = DateTime.now();
|
||||
final db = await _database;
|
||||
await db.insert(
|
||||
metadataTable,
|
||||
metadata.toMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
final batch = db.batch();
|
||||
metadataEntries.where((metadata) => metadata != null).forEach((metadata) => batch.insert(
|
||||
metadataTable,
|
||||
metadata.toMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
));
|
||||
await batch.commit(noResult: true);
|
||||
debugPrint('$runtimeType saveMetadata complete in ${DateTime.now().difference(start).inMilliseconds}ms with ${metadataEntries.length} entries');
|
||||
}
|
||||
|
||||
Future<List<AddressDetails>> getAllAddresses() async {
|
||||
|
@ -78,13 +82,17 @@ class MetadataDb {
|
|||
return null;
|
||||
}
|
||||
|
||||
insertAddress(AddressDetails metadata) async {
|
||||
// debugPrint('$runtimeType insertAddress metadata=$metadata');
|
||||
saveAddresses(Iterable<AddressDetails> addresses) async {
|
||||
if (addresses == null || addresses.isEmpty) return;
|
||||
final start = DateTime.now();
|
||||
final db = await _database;
|
||||
await db.insert(
|
||||
addressTable,
|
||||
metadata.toMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
final batch = db.batch();
|
||||
addresses.where((address) => address != null).forEach((address) => batch.insert(
|
||||
addressTable,
|
||||
address.toMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
));
|
||||
await batch.commit(noResult: true);
|
||||
debugPrint('$runtimeType saveAddresses complete in ${DateTime.now().difference(start).inMilliseconds}ms with ${addresses.length} entries');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/model/image_metadata.dart';
|
||||
import 'package:aves/model/metadata_db.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
|
@ -22,7 +21,6 @@ class MetadataService {
|
|||
}
|
||||
|
||||
static Future<CatalogMetadata> getCatalogMetadata(ImageEntry entry) async {
|
||||
CatalogMetadata metadata;
|
||||
try {
|
||||
// return map with:
|
||||
// 'dateMillis': date taken in milliseconds since Epoch (long)
|
||||
|
@ -34,9 +32,7 @@ class MetadataService {
|
|||
'path': entry.path,
|
||||
}) as Map;
|
||||
result['contentId'] = entry.contentId;
|
||||
metadata = CatalogMetadata.fromMap(result);
|
||||
metadataDb.insertMetadata(metadata);
|
||||
return metadata;
|
||||
return CatalogMetadata.fromMap(result);
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint('getCatalogMetadata failed with exception=${e.message}');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue