diff --git a/lib/models/file.dart b/lib/models/file.dart index e12d0f617..ae5cd0fff 100644 --- a/lib/models/file.dart +++ b/lib/models/file.dart @@ -285,7 +285,7 @@ class File extends EnteFile { } // return 0 if the height is not available - int get heigth { + int get height { return pubMagicMetadata?.h ?? 0; } diff --git a/lib/models/magic_metadata.dart b/lib/models/magic_metadata.dart index 83ad53798..8c752f6b7 100644 --- a/lib/models/magic_metadata.dart +++ b/lib/models/magic_metadata.dart @@ -21,6 +21,7 @@ const publicMagicKeyWidth = 'w'; const publicMagicKeyHeight = 'h'; const pubMagicKeyLat = "lat"; const pubMagicKeyLong = "long"; +const pubMotionVideoIndex = "mvi"; class MagicMetadata { // 0 -> visible @@ -52,6 +53,9 @@ class PubMagicMetadata { int? h; double? lat; double? long; + // Motion Video Index. Positive value indicates that the file is a motion + // photo + int? mvi; PubMagicMetadata({ this.editedTime, @@ -62,6 +66,7 @@ class PubMagicMetadata { this.h, this.lat, this.long, + this.mvi, }); factory PubMagicMetadata.fromEncodedJson(String encodedJson) => @@ -81,6 +86,7 @@ class PubMagicMetadata { h: map[publicMagicKeyHeight], lat: map[pubMagicKeyLat], long: map[pubMagicKeyLong], + mvi: map[pubMotionVideoIndex], ); } } diff --git a/lib/ui/viewer/file/thumbnail_widget.dart b/lib/ui/viewer/file/thumbnail_widget.dart index e5b787465..f66335045 100644 --- a/lib/ui/viewer/file/thumbnail_widget.dart +++ b/lib/ui/viewer/file/thumbnail_widget.dart @@ -111,8 +111,9 @@ class _ThumbnailWidgetState extends State { } if (widget.file!.fileType == FileType.video) { contentChildren.add(const VideoOverlayIcon()); - } else if (widget.file!.fileType == FileType.livePhoto && - widget.shouldShowLivePhotoOverlay) { + } else if (widget.shouldShowLivePhotoOverlay && + (widget.file!.fileType == FileType.livePhoto || + ((widget.file!.pubMagicMetadata?.mvi ?? 0) > 0))) { contentChildren.add(const LivePhotoOverlayIcon()); } if (widget.shouldShowOwnerAvatar) { diff --git a/lib/ui/viewer/file/zoomable_image.dart b/lib/ui/viewer/file/zoomable_image.dart index 687efb7e5..9087e0250 100644 --- a/lib/ui/viewer/file/zoomable_image.dart +++ b/lib/ui/viewer/file/zoomable_image.dart @@ -276,7 +276,7 @@ class _ZoomableImageState extends State final int h = imageInfo.image.height, w = imageInfo.image.width; if (h != 0 && w != 0 && - (h != widget.photo.heigth || w != widget.photo.width)) { + (h != widget.photo.height || w != widget.photo.width)) { _logger.info('Updating aspect ratio for ${widget.photo} to $h:$w'); await FileMagicService.instance.updatePublicMagicMetadata( [widget.photo], {publicMagicKeyHeight: h, publicMagicKeyWidth: w}); diff --git a/lib/ui/viewer/file/zoomable_live_image.dart b/lib/ui/viewer/file/zoomable_live_image.dart index 929e95847..cb2ebd060 100644 --- a/lib/ui/viewer/file/zoomable_live_image.dart +++ b/lib/ui/viewer/file/zoomable_live_image.dart @@ -5,10 +5,13 @@ import 'package:chewie/chewie.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:motion_photos/motion_photos.dart'; +import "package:photos/core/configuration.dart"; import 'package:photos/core/constants.dart'; import "package:photos/generated/l10n.dart"; import 'package:photos/models/file.dart'; import "package:photos/models/file_type.dart"; +import "package:photos/models/magic_metadata.dart"; +import "package:photos/services/file_magic_service.dart"; import 'package:photos/ui/viewer/file/zoomable_image.dart'; import 'package:photos/utils/file_util.dart'; import 'package:photos/utils/toast_util.dart'; @@ -177,6 +180,13 @@ class _ZoomableLiveImageState extends State final motionPhoto = MotionPhotos(imageFile.path); final index = motionPhoto.getMotionVideoIndex(); if (index != null) { + if (widget.file.pubMagicMetadata?.mvi == null && + (widget.file.ownerID ?? 0) == Configuration.instance.getUserID()!) { + FileMagicService.instance.updatePublicMagicMetadata( + [widget.file], + {pubMotionVideoIndex: index.start}, + ).ignore(); + } return motionPhoto.getMotionVideoFile( index: index, ); diff --git a/lib/utils/file_uploader.dart b/lib/utils/file_uploader.dart index 2eaef0a1d..d4c79bf94 100644 --- a/lib/utils/file_uploader.dart +++ b/lib/utils/file_uploader.dart @@ -458,12 +458,17 @@ class FileUploader { MetadataRequest? pubMetadataRequest; if ((mediaUploadData.height ?? 0) != 0 && (mediaUploadData.width ?? 0) != 0) { + final pubMetadata = { + publicMagicKeyHeight: mediaUploadData.height, + publicMagicKeyWidth: mediaUploadData.width + }; + if (mediaUploadData.motionPhotoStartIndex != null) { + pubMetadata[pubMotionVideoIndex] = + mediaUploadData.motionPhotoStartIndex; + } pubMetadataRequest = await getPubMetadataRequest( file, - { - publicMagicKeyHeight: mediaUploadData.height, - publicMagicKeyWidth: mediaUploadData.width - }, + pubMetadata, fileAttributes.key!, ); } diff --git a/lib/utils/file_uploader_util.dart b/lib/utils/file_uploader_util.dart index c6b8e0a39..3f0e71644 100644 --- a/lib/utils/file_uploader_util.dart +++ b/lib/utils/file_uploader_util.dart @@ -6,6 +6,7 @@ import 'dart:ui' as ui; import 'package:archive/archive_io.dart'; import 'package:logging/logging.dart'; +import "package:motion_photos/motion_photos.dart"; import 'package:motionphoto/motionphoto.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; @@ -33,6 +34,9 @@ class MediaUploadData { final FileHashData? hashData; final int? height; final int? width; + // For android motion photos, the startIndex is the index of the first frame + // For iOS, this value will be always null. + final int? motionPhotoStartIndex; MediaUploadData( this.sourceFile, @@ -41,6 +45,7 @@ class MediaUploadData { this.hashData, { this.height, this.width, + this.motionPhotoStartIndex, }); } @@ -154,6 +159,15 @@ Future _getMediaUploadDataFromAssetFile(ente.File file) async { h = asset.height; w = asset.width; } + int? motionPhotoStartingIndex; + if (io.Platform.isAndroid && asset.type == AssetType.image) { + try { + motionPhotoStartingIndex = + MotionPhotos(sourceFile.path).getMotionVideoIndex()?.start; + } catch (e) { + _logger.severe('error while detecthing motion photo start index', e); + } + } return MediaUploadData( sourceFile, thumbnailData, @@ -161,6 +175,7 @@ Future _getMediaUploadDataFromAssetFile(ente.File file) async { FileHashData(fileHash, zipHash: zipHash), height: h, width: w, + motionPhotoStartIndex: motionPhotoStartingIndex, ); } diff --git a/pubspec.yaml b/pubspec.yaml index 75a04cfd0..285c9afbf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.7.45+445 +version: 0.7.47+447 environment: sdk: '>=2.17.0 <3.0.0'