#158 guard DateTime against weird timestamps
This commit is contained in:
parent
dbc3b1d68d
commit
d183541b58
8 changed files with 62 additions and 27 deletions
|
@ -16,6 +16,7 @@ import 'package:aves/services/geocoding_service.dart';
|
||||||
import 'package:aves/services/metadata/svg_metadata_service.dart';
|
import 'package:aves/services/metadata/svg_metadata_service.dart';
|
||||||
import 'package:aves/theme/format.dart';
|
import 'package:aves/theme/format.dart';
|
||||||
import 'package:aves/utils/change_notifier.dart';
|
import 'package:aves/utils/change_notifier.dart';
|
||||||
|
import 'package:aves/utils/time_utils.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:country_code/country_code.dart';
|
import 'package:country_code/country_code.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -349,12 +350,17 @@ class AvesEntry {
|
||||||
|
|
||||||
DateTime? get bestDate {
|
DateTime? get bestDate {
|
||||||
if (_bestDate == null) {
|
if (_bestDate == null) {
|
||||||
if ((_catalogDateMillis ?? 0) > 0) {
|
try {
|
||||||
_bestDate = DateTime.fromMillisecondsSinceEpoch(_catalogDateMillis!);
|
if ((_catalogDateMillis ?? 0) > 0) {
|
||||||
} else if ((sourceDateTakenMillis ?? 0) > 0) {
|
_bestDate = DateTime.fromMillisecondsSinceEpoch(_catalogDateMillis!);
|
||||||
_bestDate = DateTime.fromMillisecondsSinceEpoch(sourceDateTakenMillis!);
|
} else if ((sourceDateTakenMillis ?? 0) > 0) {
|
||||||
} else if ((dateModifiedSecs ?? 0) > 0) {
|
_bestDate = DateTime.fromMillisecondsSinceEpoch(sourceDateTakenMillis!);
|
||||||
_bestDate = DateTime.fromMillisecondsSinceEpoch(dateModifiedSecs! * 1000);
|
} else if ((dateModifiedSecs ?? 0) > 0) {
|
||||||
|
_bestDate = DateTime.fromMillisecondsSinceEpoch(dateModifiedSecs! * 1000);
|
||||||
|
}
|
||||||
|
} catch (e, stack) {
|
||||||
|
// date millis may be out of range
|
||||||
|
reportService.recordError(e, stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _bestDate;
|
return _bestDate;
|
||||||
|
@ -748,13 +754,11 @@ class AvesEntry {
|
||||||
return c != 0 ? c : compareAsciiUpperCase(a.extension ?? '', b.extension ?? '');
|
return c != 0 ? c : compareAsciiUpperCase(a.extension ?? '', b.extension ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
static final _epoch = DateTime.fromMillisecondsSinceEpoch(0);
|
|
||||||
|
|
||||||
// compare by:
|
// compare by:
|
||||||
// 1) date descending
|
// 1) date descending
|
||||||
// 2) name descending
|
// 2) name descending
|
||||||
static int compareByDate(AvesEntry a, AvesEntry b) {
|
static int compareByDate(AvesEntry a, AvesEntry b) {
|
||||||
var c = (b.bestDate ?? _epoch).compareTo(a.bestDate ?? _epoch);
|
var c = (b.bestDate ?? epoch).compareTo(a.bestDate ?? epoch);
|
||||||
if (c != 0) return c;
|
if (c != 0) return c;
|
||||||
return compareByName(b, a);
|
return compareByName(b, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,13 @@ import 'package:aves/theme/format.dart';
|
||||||
import 'package:aves/utils/file_utils.dart';
|
import 'package:aves/utils/file_utils.dart';
|
||||||
import 'package:aves/utils/math_utils.dart';
|
import 'package:aves/utils/math_utils.dart';
|
||||||
import 'package:aves/utils/string_utils.dart';
|
import 'package:aves/utils/string_utils.dart';
|
||||||
|
import 'package:aves/utils/time_utils.dart';
|
||||||
import 'package:aves/widgets/viewer/video/fijkplayer.dart';
|
import 'package:aves/widgets/viewer/video/fijkplayer.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:fijkplayer/fijkplayer.dart';
|
import 'package:fijkplayer/fijkplayer.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
class VideoMetadataFormatter {
|
class VideoMetadataFormatter {
|
||||||
static final _epoch = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
|
|
||||||
static final _anotherDatePattern = RegExp(r'(\d{4})[-/](\d{2})[-/](\d{2}) (\d{2}):(\d{2}):(\d{2})');
|
static final _anotherDatePattern = RegExp(r'(\d{4})[-/](\d{2})[-/](\d{2}) (\d{2}):(\d{2}):(\d{2})');
|
||||||
static final _durationPattern = RegExp(r'(\d+):(\d+):(\d+)(.\d+)');
|
static final _durationPattern = RegExp(r'(\d+):(\d+):(\d+)(.\d+)');
|
||||||
static final _locationPattern = RegExp(r'([+-][.0-9]+)');
|
static final _locationPattern = RegExp(r'([+-][.0-9]+)');
|
||||||
|
@ -349,7 +349,7 @@ class VideoMetadataFormatter {
|
||||||
static String? _formatDate(String value) {
|
static String? _formatDate(String value) {
|
||||||
final date = DateTime.tryParse(value);
|
final date = DateTime.tryParse(value);
|
||||||
if (date == null) return value;
|
if (date == null) return value;
|
||||||
if (date == _epoch) return null;
|
if (date == epoch) return null;
|
||||||
return date.toIso8601String();
|
return date.toIso8601String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -237,7 +237,14 @@ class PlatformMetadataFetchService implements MetadataFetchService {
|
||||||
'sizeBytes': entry.sizeBytes,
|
'sizeBytes': entry.sizeBytes,
|
||||||
'field': field.toExifInterfaceTag(),
|
'field': field.toExifInterfaceTag(),
|
||||||
});
|
});
|
||||||
if (result is int) return DateTime.fromMillisecondsSinceEpoch(result);
|
if (result is int) {
|
||||||
|
try {
|
||||||
|
return DateTime.fromMillisecondsSinceEpoch(result);
|
||||||
|
} catch (e, stack) {
|
||||||
|
// date millis may be out of range
|
||||||
|
await reportService.recordError(e, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
} on PlatformException catch (e, stack) {
|
} on PlatformException catch (e, stack) {
|
||||||
if (!entry.isMissingAtPath) {
|
if (!entry.isMissingAtPath) {
|
||||||
await reportService.recordError(e, stack);
|
await reportService.recordError(e, stack);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:aves/services/common/services.dart';
|
||||||
|
|
||||||
extension ExtraDateTime on DateTime {
|
extension ExtraDateTime on DateTime {
|
||||||
bool isAtSameYearAs(DateTime? other) => year == other?.year;
|
bool isAtSameYearAs(DateTime? other) => year == other?.year;
|
||||||
|
|
||||||
|
@ -14,6 +16,8 @@ extension ExtraDateTime on DateTime {
|
||||||
bool get isThisYear => isAtSameYearAs(DateTime.now());
|
bool get isThisYear => isAtSameYearAs(DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final epoch = DateTime.fromMillisecondsSinceEpoch(0, isUtc: true);
|
||||||
|
|
||||||
final _unixStampMillisPattern = RegExp(r'\d{13}');
|
final _unixStampMillisPattern = RegExp(r'\d{13}');
|
||||||
final _unixStampSecPattern = RegExp(r'\d{10}');
|
final _unixStampSecPattern = RegExp(r'\d{10}');
|
||||||
final _plainPattern = RegExp(r'(\d{8})([_-\s](\d{6})([_-\s](\d{3}))?)?');
|
final _plainPattern = RegExp(r'(\d{8})([_-\s](\d{6})([_-\s](\d{3}))?)?');
|
||||||
|
@ -23,22 +27,32 @@ DateTime? parseUnknownDateFormat(String? s) {
|
||||||
|
|
||||||
var match = _unixStampMillisPattern.firstMatch(s);
|
var match = _unixStampMillisPattern.firstMatch(s);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
final stampString = match.group(0);
|
final stampMillisString = match.group(0);
|
||||||
if (stampString != null) {
|
if (stampMillisString != null) {
|
||||||
final stampMillis = int.tryParse(stampString);
|
final stampMillis = int.tryParse(stampMillisString);
|
||||||
if (stampMillis != null) {
|
if (stampMillis != null) {
|
||||||
return DateTime.fromMillisecondsSinceEpoch(stampMillis, isUtc: false);
|
try {
|
||||||
|
return DateTime.fromMillisecondsSinceEpoch(stampMillis, isUtc: false);
|
||||||
|
} catch (e, stack) {
|
||||||
|
// date millis may be out of range
|
||||||
|
reportService.recordError(e, stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match = _unixStampSecPattern.firstMatch(s);
|
match = _unixStampSecPattern.firstMatch(s);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
final stampString = match.group(0);
|
final stampSecString = match.group(0);
|
||||||
if (stampString != null) {
|
if (stampSecString != null) {
|
||||||
final stampMillis = int.tryParse(stampString);
|
final stampSec = int.tryParse(stampSecString);
|
||||||
if (stampMillis != null) {
|
if (stampSec != null) {
|
||||||
return DateTime.fromMillisecondsSinceEpoch(stampMillis * 1000, isUtc: false);
|
try {
|
||||||
|
return DateTime.fromMillisecondsSinceEpoch(stampSec * 1000, isUtc: false);
|
||||||
|
} catch (e, stack) {
|
||||||
|
// date millis may be out of range
|
||||||
|
reportService.recordError(e, stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,18 +76,18 @@ class _RenderSliverKnownExtentBoxAdaptor extends RenderSliverMultiBoxAdaptor {
|
||||||
|
|
||||||
SectionLayout? sectionAtIndex(int index) => sectionLayouts.firstWhereOrNull((section) => section.hasChild(index));
|
SectionLayout? sectionAtIndex(int index) => sectionLayouts.firstWhereOrNull((section) => section.hasChild(index));
|
||||||
|
|
||||||
SectionLayout sectionAtOffset(double scrollOffset) => sectionLayouts.firstWhere((section) => section.hasChildAtOffset(scrollOffset), orElse: () => sectionLayouts.last);
|
SectionLayout? sectionAtOffset(double scrollOffset) => sectionLayouts.firstWhereOrNull((section) => section.hasChildAtOffset(scrollOffset)) ?? sectionLayouts.lastOrNull;
|
||||||
|
|
||||||
double indexToLayoutOffset(int index) {
|
double indexToLayoutOffset(int index) {
|
||||||
return (sectionAtIndex(index) ?? sectionLayouts.last).indexToLayoutOffset(index);
|
return (sectionAtIndex(index) ?? sectionLayouts.last).indexToLayoutOffset(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
||||||
return sectionAtOffset(scrollOffset).getMinChildIndexForScrollOffset(scrollOffset);
|
return sectionAtOffset(scrollOffset)?.getMinChildIndexForScrollOffset(scrollOffset) ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
||||||
return sectionAtOffset(scrollOffset).getMaxChildIndexForScrollOffset(scrollOffset);
|
return sectionAtOffset(scrollOffset)?.getMaxChildIndexForScrollOffset(scrollOffset) ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double estimateMaxScrollOffset(
|
double estimateMaxScrollOffset(
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
import 'package:aves/model/source/enums.dart';
|
import 'package:aves/model/source/enums.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/utils/time_utils.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
import 'package:aves/widgets/common/providers/selection_provider.dart';
|
import 'package:aves/widgets/common/providers/selection_provider.dart';
|
||||||
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
|
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
|
||||||
|
@ -63,7 +65,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compareFiltersByDate(FilterGridItem<CollectionFilter> a, FilterGridItem<CollectionFilter> b) {
|
static int compareFiltersByDate(FilterGridItem<CollectionFilter> a, FilterGridItem<CollectionFilter> b) {
|
||||||
final c = (b.entry?.bestDate ?? DateTime.fromMillisecondsSinceEpoch(0)).compareTo(a.entry?.bestDate ?? DateTime.fromMillisecondsSinceEpoch(0));
|
final c = (b.entry?.bestDate ?? epoch).compareTo(a.entry?.bestDate ?? epoch);
|
||||||
return c != 0 ? c : a.filter.compareTo(b.filter);
|
return c != 0 ? c : a.filter.compareTo(b.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,11 @@ class ViewerDebugPage extends StatelessWidget {
|
||||||
String toDateValue(int? time, {int factor = 1}) {
|
String toDateValue(int? time, {int factor = 1}) {
|
||||||
var value = '$time';
|
var value = '$time';
|
||||||
if (time != null && time > 0) {
|
if (time != null && time > 0) {
|
||||||
value += ' (${DateTime.fromMillisecondsSinceEpoch(time * factor)})';
|
try {
|
||||||
|
value += ' (${DateTime.fromMillisecondsSinceEpoch(time * factor)})';
|
||||||
|
} catch (e) {
|
||||||
|
value += ' (invalid DateTime})';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,11 @@ class _MetadataTabState extends State<MetadataTab> {
|
||||||
if (secondTimestampKeys.contains(key)) {
|
if (secondTimestampKeys.contains(key)) {
|
||||||
v *= 1000;
|
v *= 1000;
|
||||||
}
|
}
|
||||||
value += ' (${DateTime.fromMillisecondsSinceEpoch(v)})';
|
try {
|
||||||
|
value += ' (${DateTime.fromMillisecondsSinceEpoch(v)})';
|
||||||
|
} catch (e) {
|
||||||
|
value += ' (invalid DateTime})';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (key == 'xmp' && v != null && v is Uint8List) {
|
if (key == 'xmp' && v != null && v is Uint8List) {
|
||||||
value = String.fromCharCodes(v);
|
value = String.fromCharCodes(v);
|
||||||
|
|
Loading…
Reference in a new issue