load metadata/address on startup
This commit is contained in:
parent
27bf3f4dad
commit
6206dbde62
6 changed files with 55 additions and 42 deletions
|
@ -47,11 +47,14 @@ class _HomePageState extends State<HomePage> {
|
|||
await metadataDb.init();
|
||||
|
||||
eventChannel.receiveBroadcastStream().cast<Map>().listen(
|
||||
(entryMap) => setState(() => entries.add(ImageEntry.fromMap(entryMap))),
|
||||
(entryMap) => entries.add(ImageEntry.fromMap(entryMap)),
|
||||
onDone: () async {
|
||||
debugPrint('mediastore stream done');
|
||||
await loadCatalogMetadata();
|
||||
setState(() {});
|
||||
await catalogEntries();
|
||||
setState(() {});
|
||||
await loadAddresses();
|
||||
await locateEntries();
|
||||
},
|
||||
onError: (error) => debugPrint('mediastore stream error=$error'),
|
||||
|
@ -69,10 +72,36 @@ class _HomePageState extends State<HomePage> {
|
|||
);
|
||||
}
|
||||
|
||||
loadCatalogMetadata() async {
|
||||
debugPrint('$runtimeType loadCatalogMetadata start');
|
||||
final start = DateTime.now();
|
||||
final saved = await metadataDb.loadMetadataEntries();
|
||||
entries.forEach((entry) {
|
||||
final contentId = entry.contentId;
|
||||
if (contentId != null) {
|
||||
entry.catalogMetadata = saved.firstWhere((metadata) => metadata.contentId == contentId, orElse: () => null);
|
||||
}
|
||||
});
|
||||
debugPrint('$runtimeType loadCatalogMetadata complete in ${DateTime.now().difference(start).inSeconds}s with ${saved.length} saved entries');
|
||||
}
|
||||
|
||||
loadAddresses() async {
|
||||
debugPrint('$runtimeType loadAddresses start');
|
||||
final start = DateTime.now();
|
||||
final saved = await metadataDb.loadAddresses();
|
||||
entries.forEach((entry) {
|
||||
final contentId = entry.contentId;
|
||||
if (contentId != null) {
|
||||
entry.addressDetails = saved.firstWhere((address) => address.contentId == contentId, orElse: () => null);
|
||||
}
|
||||
});
|
||||
debugPrint('$runtimeType loadAddresses complete in ${DateTime.now().difference(start).inSeconds}s with ${saved.length} saved entries');
|
||||
}
|
||||
|
||||
catalogEntries() async {
|
||||
debugPrint('$runtimeType catalogEntries start');
|
||||
final start = DateTime.now();
|
||||
final uncataloguedEntries = entries.where((entry) => !entry.isCataloged);
|
||||
final uncataloguedEntries = entries.where((entry) => !entry.isCatalogued);
|
||||
final newMetadata = List<CatalogMetadata>();
|
||||
await Future.forEach<ImageEntry>(uncataloguedEntries, (entry) async {
|
||||
await entry.catalog();
|
||||
|
@ -82,7 +111,6 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
// sort with more accurate date
|
||||
entries.sort((a, b) => b.bestDate.compareTo(a.bestDate));
|
||||
setState(() {});
|
||||
|
||||
metadataDb.saveMetadata(List.unmodifiable(newMetadata));
|
||||
}
|
||||
|
@ -100,6 +128,6 @@ class _HomePageState extends State<HomePage> {
|
|||
newAddresses.clear();
|
||||
}
|
||||
});
|
||||
debugPrint('$runtimeType locateEntries complete in ${DateTime.now().difference(start).inSeconds}s with ${newAddresses.length} new addresses');
|
||||
debugPrint('$runtimeType locateEntries complete in ${DateTime.now().difference(start).inSeconds}s');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,11 +86,11 @@ class ImageEntry with ChangeNotifier {
|
|||
|
||||
bool get isVideo => mimeType.startsWith(MimeTypes.MIME_VIDEO);
|
||||
|
||||
bool get isCataloged => catalogMetadata != null;
|
||||
bool get isCatalogued => catalogMetadata != null;
|
||||
|
||||
double get aspectRatio {
|
||||
if (width == 0 || height == 0) return 1;
|
||||
if (isVideo && isCataloged) {
|
||||
if (isVideo && isCatalogued) {
|
||||
if (catalogMetadata.videoRotation % 180 == 90) return height / width;
|
||||
}
|
||||
return width / height;
|
||||
|
@ -125,18 +125,18 @@ class ImageEntry with ChangeNotifier {
|
|||
return '${d.inHours}:$twoDigitMinutes:$twoDigitSeconds';
|
||||
}
|
||||
|
||||
bool get hasGps => isCataloged && catalogMetadata.latitude != null;
|
||||
bool get hasGps => isCatalogued && catalogMetadata.latitude != null;
|
||||
|
||||
bool get isLocated => addressDetails != null;
|
||||
|
||||
Tuple2<double, double> get latLng => isCataloged ? Tuple2(catalogMetadata.latitude, catalogMetadata.longitude) : null;
|
||||
Tuple2<double, double> get latLng => isCatalogued ? Tuple2(catalogMetadata.latitude, catalogMetadata.longitude) : null;
|
||||
|
||||
String get geoUri => hasGps ? 'geo:${catalogMetadata.latitude},${catalogMetadata.longitude}' : null;
|
||||
|
||||
List<String> get xmpSubjects => catalogMetadata?.xmpSubjects?.split(';')?.where((tag) => tag.isNotEmpty)?.toList() ?? [];
|
||||
|
||||
catalog() async {
|
||||
if (isCataloged) return;
|
||||
if (isCatalogued) return;
|
||||
catalogMetadata = await MetadataService.getCatalogMetadata(this);
|
||||
notifyListeners();
|
||||
}
|
||||
|
|
|
@ -34,21 +34,13 @@ class MetadataDb {
|
|||
await init();
|
||||
}
|
||||
|
||||
Future<List<CatalogMetadata>> getAllMetadata() async {
|
||||
debugPrint('$runtimeType getAllMetadata');
|
||||
Future<List<CatalogMetadata>> loadMetadataEntries() async {
|
||||
final start = DateTime.now();
|
||||
final db = await _database;
|
||||
final maps = await db.query(metadataTable);
|
||||
return maps.map((map) => CatalogMetadata.fromMap(map)).toList();
|
||||
}
|
||||
|
||||
Future<CatalogMetadata> getMetadata(int contentId) async {
|
||||
debugPrint('$runtimeType getMetadata contentId=$contentId');
|
||||
final db = await _database;
|
||||
List<Map> maps = await db.query(metadataTable, where: 'contentId = ?', whereArgs: [contentId]);
|
||||
if (maps.length > 0) {
|
||||
return CatalogMetadata.fromMap(maps.first);
|
||||
}
|
||||
return null;
|
||||
final metadataEntries = maps.map((map) => CatalogMetadata.fromMap(map)).toList();
|
||||
debugPrint('$runtimeType loadMetadataEntries complete in ${DateTime.now().difference(start).inMilliseconds}ms with ${metadataEntries.length} entries');
|
||||
return metadataEntries;
|
||||
}
|
||||
|
||||
saveMetadata(Iterable<CatalogMetadata> metadataEntries) async {
|
||||
|
@ -65,21 +57,13 @@ class MetadataDb {
|
|||
debugPrint('$runtimeType saveMetadata complete in ${DateTime.now().difference(start).inMilliseconds}ms with ${metadataEntries.length} entries');
|
||||
}
|
||||
|
||||
Future<List<AddressDetails>> getAllAddresses() async {
|
||||
debugPrint('$runtimeType getAllAddresses');
|
||||
Future<List<AddressDetails>> loadAddresses() async {
|
||||
final start = DateTime.now();
|
||||
final db = await _database;
|
||||
final maps = await db.query(addressTable);
|
||||
return maps.map((map) => AddressDetails.fromMap(map)).toList();
|
||||
}
|
||||
|
||||
Future<AddressDetails> getAddress(int contentId) async {
|
||||
debugPrint('$runtimeType getAddress contentId=$contentId');
|
||||
final db = await _database;
|
||||
List<Map> maps = await db.query(addressTable, where: 'contentId = ?', whereArgs: [contentId]);
|
||||
if (maps.length > 0) {
|
||||
return AddressDetails.fromMap(maps.first);
|
||||
}
|
||||
return null;
|
||||
final addresses = maps.map((map) => AddressDetails.fromMap(map)).toList();
|
||||
debugPrint('$runtimeType loadAddresses complete in ${DateTime.now().difference(start).inMilliseconds}ms with ${addresses.length} entries');
|
||||
return addresses;
|
||||
}
|
||||
|
||||
saveAddresses(Iterable<AddressDetails> addresses) async {
|
||||
|
|
|
@ -22,15 +22,15 @@ class DebugPageState extends State<DebugPage> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_dbMetadataLoader = metadataDb.getAllMetadata();
|
||||
_dbAddressLoader = metadataDb.getAllAddresses();
|
||||
_dbMetadataLoader = metadataDb.loadMetadataEntries();
|
||||
_dbAddressLoader = metadataDb.loadAddresses();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Map<String, List<ImageEntry>> byMimeTypes = groupBy(entries, (entry) => entry.mimeType);
|
||||
final cataloged = entries.where((entry) => entry.isCataloged);
|
||||
final withGps = cataloged.where((entry) => entry.hasGps);
|
||||
final catalogued = entries.where((entry) => entry.isCatalogued);
|
||||
final withGps = catalogued.where((entry) => entry.hasGps);
|
||||
final located = withGps.where((entry) => entry.isLocated);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
@ -41,7 +41,7 @@ class DebugPageState extends State<DebugPage> {
|
|||
children: [
|
||||
Text('Entries: ${entries.length}'),
|
||||
...byMimeTypes.keys.map((mimeType) => Text('- $mimeType: ${byMimeTypes[mimeType].length}')),
|
||||
Text('Cataloged: ${cataloged.length}'),
|
||||
Text('Catalogued: ${catalogued.length}'),
|
||||
Text('With GPS: ${withGps.length}'),
|
||||
Text('With address: ${located.length}'),
|
||||
Divider(),
|
||||
|
|
|
@ -41,6 +41,7 @@ class ImageMapState extends State<ImageMap> with AutomaticKeepAliveClientMixin {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final accentHue = HSVColor.fromColor(Theme.of(context).accentColor).hue;
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
child: ClipRRect(
|
||||
|
@ -55,7 +56,7 @@ class ImageMapState extends State<ImageMap> with AutomaticKeepAliveClientMixin {
|
|||
markers: [
|
||||
Marker(
|
||||
markerId: MarkerId(widget.markerId),
|
||||
icon: BitmapDescriptor.defaultMarker,
|
||||
icon: BitmapDescriptor.defaultMarkerWithHue(accentHue),
|
||||
position: widget.latLng,
|
||||
)
|
||||
].toSet(),
|
||||
|
|
|
@ -21,7 +21,7 @@ class XmpTagSection extends AnimatedWidget {
|
|||
.map((tag) => Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Chip(
|
||||
backgroundColor: Colors.indigo,
|
||||
backgroundColor: Theme.of(context).accentColor,
|
||||
label: Text(tag),
|
||||
),
|
||||
))
|
||||
|
|
Loading…
Reference in a new issue