import 'dart:async'; import 'package:aves/model/entry.dart'; import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/enums.dart'; import 'package:aves/model/metadata/fields.dart'; import 'package:aves/services/common/services.dart'; import 'package:collection/collection.dart'; import 'package:flutter/services.dart'; abstract class MetadataEditService { Future> rotate(AvesEntry entry, {required bool clockwise}); Future> flip(AvesEntry entry); Future> editExifDate(AvesEntry entry, DateModifier modifier); Future> editMetadata(AvesEntry entry, Map modifier, {bool autoCorrectTrailerOffset = true}); Future> removeTrailerVideo(AvesEntry entry); Future> removeTypes(AvesEntry entry, Set types); } class PlatformMetadataEditService implements MetadataEditService { static const _platform = MethodChannel('deckers.thibault/aves/metadata_edit'); @override Future> rotate(AvesEntry entry, {required bool clockwise}) async { try { // returns map with: 'rotationDegrees' 'isFlipped' final result = await _platform.invokeMethod('rotate', { 'entry': entry.toPlatformEntryMap(), 'clockwise': clockwise, }); if (result != null) return (result as Map).cast(); } on PlatformException catch (e, stack) { if (!entry.isMissingAtPath) { await reportService.recordError(e, stack); } } return {}; } @override Future> flip(AvesEntry entry) async { try { // returns map with: 'rotationDegrees' 'isFlipped' final result = await _platform.invokeMethod('flip', { 'entry': entry.toPlatformEntryMap(), }); if (result != null) return (result as Map).cast(); } on PlatformException catch (e, stack) { if (!entry.isMissingAtPath) { await reportService.recordError(e, stack); } } return {}; } @override Future> editExifDate(AvesEntry entry, DateModifier modifier) async { try { final result = await _platform.invokeMethod('editDate', { 'entry': entry.toPlatformEntryMap(), 'dateMillis': modifier.setDateTime?.millisecondsSinceEpoch, 'shiftMinutes': modifier.shiftMinutes, 'fields': modifier.fields.where((v) => v.type == MetadataType.exif).map((v) => v.exifInterfaceTag).whereNotNull().toList(), }); if (result != null) return (result as Map).cast(); } on PlatformException catch (e, stack) { if (!entry.isMissingAtPath) { await reportService.recordError(e, stack); } } return {}; } @override Future> editMetadata( AvesEntry entry, Map metadata, { bool autoCorrectTrailerOffset = true, }) async { try { final result = await _platform.invokeMethod('editMetadata', { 'entry': entry.toPlatformEntryMap(), 'metadata': metadata.map((type, value) => MapEntry(_toPlatformMetadataType(type), value)), 'autoCorrectTrailerOffset': autoCorrectTrailerOffset, }); if (result != null) return (result as Map).cast(); } on PlatformException catch (e, stack) { if (!entry.isMissingAtPath) { await reportService.recordError(e, stack); } } return {}; } @override Future> removeTrailerVideo(AvesEntry entry) async { try { final result = await _platform.invokeMethod('removeTrailerVideo', { 'entry': entry.toPlatformEntryMap(), }); if (result != null) return (result as Map).cast(); } on PlatformException catch (e, stack) { if (!entry.isMissingAtPath) { await reportService.recordError(e, stack); } } return {}; } @override Future> removeTypes(AvesEntry entry, Set types) async { try { final result = await _platform.invokeMethod('removeTypes', { 'entry': entry.toPlatformEntryMap(), 'types': types.map(_toPlatformMetadataType).toList(), }); if (result != null) return (result as Map).cast(); } on PlatformException catch (e, stack) { if (!entry.isMissingAtPath) { await reportService.recordError(e, stack); } } return {}; } String _toPlatformMetadataType(MetadataType type) { switch (type) { case MetadataType.comment: return 'comment'; case MetadataType.exif: return 'exif'; case MetadataType.iccProfile: return 'icc_profile'; case MetadataType.iptc: return 'iptc'; case MetadataType.jfif: return 'jfif'; case MetadataType.jpegAdobe: return 'jpeg_adobe'; case MetadataType.jpegDucky: return 'jpeg_ducky'; case MetadataType.photoshopIrb: return 'photoshop_irb'; case MetadataType.xmp: return 'xmp'; } } }