#1507 mime type normalization
This commit is contained in:
parent
e8eae7e9db
commit
4df4738dd3
11 changed files with 45 additions and 19 deletions
|
@ -179,7 +179,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb {
|
|||
void _batchInsertEntry(Batch batch, AvesEntry entry) {
|
||||
batch.insert(
|
||||
entryTable,
|
||||
entry.toMap(),
|
||||
entry.toDatabaseMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ class AvesEntry with AvesEntryBase {
|
|||
}
|
||||
|
||||
// for DB only
|
||||
Map<String, dynamic> toMap() {
|
||||
Map<String, dynamic> toDatabaseMap() {
|
||||
return {
|
||||
EntryFields.id: id,
|
||||
EntryFields.uri: uri,
|
||||
|
@ -397,6 +397,17 @@ class AvesEntry with AvesEntryBase {
|
|||
}.nonNulls.where((v) => v.isNotEmpty).join(', ');
|
||||
}
|
||||
|
||||
static void normalizeMimeTypeFields(Map fields) {
|
||||
final mimeType = fields[EntryFields.mimeType] as String?;
|
||||
if (mimeType != null) {
|
||||
fields[EntryFields.mimeType] = MimeTypes.normalize(mimeType);
|
||||
}
|
||||
final sourceMimeType = fields[EntryFields.sourceMimeType] as String?;
|
||||
if (sourceMimeType != null) {
|
||||
fields[EntryFields.sourceMimeType] = MimeTypes.normalize(sourceMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> applyNewFields(Map newFields, {required bool persist}) async {
|
||||
final oldMimeType = mimeType;
|
||||
final oldDateModifiedMillis = this.dateModifiedMillis;
|
||||
|
@ -458,7 +469,7 @@ class AvesEntry with AvesEntryBase {
|
|||
|
||||
final updatedEntry = await mediaFetchService.getEntry(uri, mimeType);
|
||||
if (updatedEntry != null) {
|
||||
await applyNewFields(updatedEntry.toMap(), persist: persist);
|
||||
await applyNewFields(updatedEntry.toDatabaseMap(), persist: persist);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,9 +114,7 @@ class VideoMetadataFormatter {
|
|||
|
||||
// exclude date if it is suspiciously close to epoch
|
||||
if (dateMillis != null && !DateTime.fromMillisecondsSinceEpoch(dateMillis).isAtSameDayAs(epoch)) {
|
||||
catalogMetadata = catalogMetadata.copyWith(
|
||||
dateMillis: dateMillis,
|
||||
);
|
||||
catalogMetadata = catalogMetadata.copyWith(dateMillis: dateMillis);
|
||||
}
|
||||
|
||||
return catalogMetadata;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/keys.dart';
|
||||
import 'package:aves/model/entry/extensions/multipage.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
|
@ -81,17 +82,17 @@ class MultiPageInfo {
|
|||
final videoPage = _pages.firstWhereOrNull((page) => page.isVideo);
|
||||
if (videoPage != null && videoPage.uri == null) {
|
||||
final fields = await embeddedDataService.extractMotionPhotoVideo(mainEntry);
|
||||
if (fields.containsKey('uri')) {
|
||||
if (fields.containsKey(EntryFields.uri)) {
|
||||
final pageIndex = _pages.indexOf(videoPage);
|
||||
_pages.removeAt(pageIndex);
|
||||
_pages.insert(
|
||||
pageIndex,
|
||||
videoPage.copyWith(
|
||||
uri: fields['uri'] as String?,
|
||||
uri: fields[EntryFields.uri] as String?,
|
||||
// the initial fake page may contain inaccurate values for the following fields
|
||||
// so we override them with values from the extracted standalone video
|
||||
rotationDegrees: fields['sourceRotationDegrees'] as int?,
|
||||
durationMillis: fields['durationMillis'] as int?,
|
||||
rotationDegrees: fields[EntryFields.sourceRotationDegrees] as int?,
|
||||
durationMillis: fields[EntryFields.durationMillis] as int?,
|
||||
));
|
||||
_pageEntries.remove(videoPage);
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ class MimeTypes {
|
|||
|
||||
static bool isVisual(String mimeType) => isImage(mimeType) || isVideo(mimeType);
|
||||
|
||||
static String _collapsedType(String mimeType) {
|
||||
static String normalize(String mimeType) {
|
||||
switch (mimeType) {
|
||||
case avi:
|
||||
case aviMSVideo:
|
||||
|
@ -127,7 +127,7 @@ class MimeTypes {
|
|||
}
|
||||
}
|
||||
|
||||
static bool refersToSameType(String a, b) => _collapsedType(a) == _collapsedType(b);
|
||||
static bool refersToSameType(String a, b) => normalize(a) == normalize(b);
|
||||
|
||||
static String? forExtension(String extension) {
|
||||
switch (extension) {
|
||||
|
|
|
@ -81,6 +81,7 @@ class PlatformMediaFetchService implements MediaFetchService {
|
|||
'mimeType': mimeType,
|
||||
'allowUnsized': allowUnsized,
|
||||
}) as Map;
|
||||
AvesEntry.normalizeMimeTypeFields(result);
|
||||
return AvesEntry.fromMap(result);
|
||||
} on PlatformException catch (e, stack) {
|
||||
// do not report issues with media content as it is likely an obsolete Media Store entry
|
||||
|
|
|
@ -85,7 +85,11 @@ class PlatformMediaStoreService implements MediaStoreService {
|
|||
'directory': directory,
|
||||
})
|
||||
.where((event) => event is Map)
|
||||
.map((event) => AvesEntry.fromMap(event as Map));
|
||||
.map((event) {
|
||||
final fields = event as Map;
|
||||
AvesEntry.normalizeMimeTypeFields(fields);
|
||||
return AvesEntry.fromMap(fields);
|
||||
});
|
||||
} on PlatformException catch (e, stack) {
|
||||
reportService.recordError(e, stack);
|
||||
return Stream.error(e);
|
||||
|
|
|
@ -3,10 +3,10 @@ import 'package:aves/model/entry/entry.dart';
|
|||
import 'package:aves/model/entry/extensions/multipage.dart';
|
||||
import 'package:aves/model/entry/extensions/props.dart';
|
||||
import 'package:aves/model/media/geotiff.dart';
|
||||
import 'package:aves/model/media/panorama.dart';
|
||||
import 'package:aves/model/metadata/catalog.dart';
|
||||
import 'package:aves/model/metadata/overlay.dart';
|
||||
import 'package:aves/model/multipage.dart';
|
||||
import 'package:aves/model/media/panorama.dart';
|
||||
import 'package:aves/services/common/service_policy.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/services/metadata/xmp.dart';
|
||||
|
@ -88,6 +88,7 @@ class PlatformMetadataFetchService implements MetadataFetchService {
|
|||
'sizeBytes': entry.sizeBytes,
|
||||
}) as Map;
|
||||
result['id'] = entry.id;
|
||||
AvesEntry.normalizeMimeTypeFields(result);
|
||||
return CatalogMetadata.fromMap(result);
|
||||
} on PlatformException catch (e, stack) {
|
||||
if (entry.isValid) {
|
||||
|
@ -164,6 +165,7 @@ class PlatformMetadataFetchService implements MetadataFetchService {
|
|||
imagePage['height'] = entry.height;
|
||||
imagePage['rotationDegrees'] = entry.rotationDegrees;
|
||||
}
|
||||
pageMaps.forEach(AvesEntry.normalizeMimeTypeFields);
|
||||
return MultiPageInfo.fromPageMaps(entry, pageMaps);
|
||||
} on PlatformException catch (e, stack) {
|
||||
if (entry.isValid) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:math';
|
|||
import 'package:aves/app_mode.dart';
|
||||
import 'package:aves/model/covers.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/filters/mime.dart';
|
||||
import 'package:aves/model/filters/rating.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
|
@ -106,12 +107,18 @@ class AvesFilterChip extends StatefulWidget {
|
|||
const touchArea = Size(kMinInteractiveDimension, kMinInteractiveDimension);
|
||||
final actionDelegate = ChipActionDelegate();
|
||||
final animations = context.read<Settings>().accessibilityAnimations;
|
||||
|
||||
var title = filter.getLabel(context);
|
||||
if (filter is MimeFilter) {
|
||||
title += ' (${filter.mime})';
|
||||
}
|
||||
|
||||
final selectedAction = await showMenu<ChipAction>(
|
||||
context: context,
|
||||
position: RelativeRect.fromRect(tapPosition & touchArea, Offset.zero & overlay.size),
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
child: Text(filter.getLabel(context)),
|
||||
child: Text(title),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
...ChipAction.values.where((action) => actionDelegate.isVisible(action, filter: filter)).map((action) {
|
||||
|
|
|
@ -103,7 +103,7 @@ class _DbTabState extends State<DbTab> {
|
|||
child: const Text('Duplicate entry'),
|
||||
),
|
||||
InfoRowGroup(
|
||||
info: Map.fromEntries(data.toMap().entries.map((kv) => MapEntry(kv.key, kv.value?.toString() ?? ''))),
|
||||
info: Map.fromEntries(data.toDatabaseMap().entries.map((kv) => MapEntry(kv.key, kv.value?.toString() ?? ''))),
|
||||
),
|
||||
],
|
||||
],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/keys.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||
|
@ -51,13 +52,14 @@ class EmbeddedDataOpener extends StatelessWidget with FeedbackMixin {
|
|||
case EmbeddedDataSource.xmp:
|
||||
fields = await embeddedDataService.extractXmpDataProp(entry, notification.props, notification.mimeType);
|
||||
}
|
||||
if (!fields.containsKey('mimeType') || !fields.containsKey('uri')) {
|
||||
AvesEntry.normalizeMimeTypeFields(fields);
|
||||
final mimeType = fields[EntryFields.mimeType] as String?;
|
||||
final uri = fields[EntryFields.uri] as String?;
|
||||
if (mimeType == null || uri == null) {
|
||||
showFeedback(context, FeedbackType.warn, context.l10n.viewerInfoOpenEmbeddedFailureFeedback);
|
||||
return;
|
||||
}
|
||||
|
||||
final mimeType = fields['mimeType']!;
|
||||
final uri = fields['uri']!;
|
||||
if (!MimeTypes.isImage(mimeType) && !MimeTypes.isVideo(mimeType)) {
|
||||
// open with another app
|
||||
unawaited(appService.open(uri, mimeType, forceChooser: true).then((success) {
|
||||
|
|
Loading…
Reference in a new issue