Parcourir la source

Motion Photo Support (#1058)

Neeraj Gupta il y a 2 ans
Parent
commit
8894c61d98

+ 1 - 1
lib/models/file.dart

@@ -285,7 +285,7 @@ class File extends EnteFile {
   }
   }
 
 
   // return 0 if the height is not available
   // return 0 if the height is not available
-  int get heigth {
+  int get height {
     return pubMagicMetadata?.h ?? 0;
     return pubMagicMetadata?.h ?? 0;
   }
   }
 
 

+ 6 - 0
lib/models/magic_metadata.dart

@@ -21,6 +21,7 @@ const publicMagicKeyWidth = 'w';
 const publicMagicKeyHeight = 'h';
 const publicMagicKeyHeight = 'h';
 const pubMagicKeyLat = "lat";
 const pubMagicKeyLat = "lat";
 const pubMagicKeyLong = "long";
 const pubMagicKeyLong = "long";
+const pubMotionVideoIndex = "mvi";
 
 
 class MagicMetadata {
 class MagicMetadata {
   // 0 -> visible
   // 0 -> visible
@@ -52,6 +53,9 @@ class PubMagicMetadata {
   int? h;
   int? h;
   double? lat;
   double? lat;
   double? long;
   double? long;
+  // Motion Video Index. Positive value indicates that the file is a motion
+  // photo
+  int? mvi;
 
 
   PubMagicMetadata({
   PubMagicMetadata({
     this.editedTime,
     this.editedTime,
@@ -62,6 +66,7 @@ class PubMagicMetadata {
     this.h,
     this.h,
     this.lat,
     this.lat,
     this.long,
     this.long,
+    this.mvi,
   });
   });
 
 
   factory PubMagicMetadata.fromEncodedJson(String encodedJson) =>
   factory PubMagicMetadata.fromEncodedJson(String encodedJson) =>
@@ -81,6 +86,7 @@ class PubMagicMetadata {
       h: map[publicMagicKeyHeight],
       h: map[publicMagicKeyHeight],
       lat: map[pubMagicKeyLat],
       lat: map[pubMagicKeyLat],
       long: map[pubMagicKeyLong],
       long: map[pubMagicKeyLong],
+      mvi: map[pubMotionVideoIndex],
     );
     );
   }
   }
 }
 }

+ 3 - 2
lib/ui/viewer/file/thumbnail_widget.dart

@@ -111,8 +111,9 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
       }
       }
       if (widget.file!.fileType == FileType.video) {
       if (widget.file!.fileType == FileType.video) {
         contentChildren.add(const VideoOverlayIcon());
         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());
         contentChildren.add(const LivePhotoOverlayIcon());
       }
       }
       if (widget.shouldShowOwnerAvatar) {
       if (widget.shouldShowOwnerAvatar) {

+ 1 - 1
lib/ui/viewer/file/zoomable_image.dart

@@ -276,7 +276,7 @@ class _ZoomableImageState extends State<ZoomableImage>
       final int h = imageInfo.image.height, w = imageInfo.image.width;
       final int h = imageInfo.image.height, w = imageInfo.image.width;
       if (h != 0 &&
       if (h != 0 &&
           w != 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');
         _logger.info('Updating aspect ratio for ${widget.photo} to $h:$w');
         await FileMagicService.instance.updatePublicMagicMetadata(
         await FileMagicService.instance.updatePublicMagicMetadata(
             [widget.photo], {publicMagicKeyHeight: h, publicMagicKeyWidth: w});
             [widget.photo], {publicMagicKeyHeight: h, publicMagicKeyWidth: w});

+ 10 - 0
lib/ui/viewer/file/zoomable_live_image.dart

@@ -5,10 +5,13 @@ import 'package:chewie/chewie.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:logging/logging.dart';
 import 'package:logging/logging.dart';
 import 'package:motion_photos/motion_photos.dart';
 import 'package:motion_photos/motion_photos.dart';
+import "package:photos/core/configuration.dart";
 import 'package:photos/core/constants.dart';
 import 'package:photos/core/constants.dart';
 import "package:photos/generated/l10n.dart";
 import "package:photos/generated/l10n.dart";
 import 'package:photos/models/file.dart';
 import 'package:photos/models/file.dart';
 import "package:photos/models/file_type.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/ui/viewer/file/zoomable_image.dart';
 import 'package:photos/utils/file_util.dart';
 import 'package:photos/utils/file_util.dart';
 import 'package:photos/utils/toast_util.dart';
 import 'package:photos/utils/toast_util.dart';
@@ -177,6 +180,13 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
       final motionPhoto = MotionPhotos(imageFile.path);
       final motionPhoto = MotionPhotos(imageFile.path);
       final index = motionPhoto.getMotionVideoIndex();
       final index = motionPhoto.getMotionVideoIndex();
       if (index != null) {
       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(
         return motionPhoto.getMotionVideoFile(
           index: index,
           index: index,
         );
         );

+ 9 - 4
lib/utils/file_uploader.dart

@@ -458,12 +458,17 @@ class FileUploader {
         MetadataRequest? pubMetadataRequest;
         MetadataRequest? pubMetadataRequest;
         if ((mediaUploadData.height ?? 0) != 0 &&
         if ((mediaUploadData.height ?? 0) != 0 &&
             (mediaUploadData.width ?? 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(
           pubMetadataRequest = await getPubMetadataRequest(
             file,
             file,
-            {
-              publicMagicKeyHeight: mediaUploadData.height,
-              publicMagicKeyWidth: mediaUploadData.width
-            },
+            pubMetadata,
             fileAttributes.key!,
             fileAttributes.key!,
           );
           );
         }
         }

+ 15 - 0
lib/utils/file_uploader_util.dart

@@ -6,6 +6,7 @@ import 'dart:ui' as ui;
 
 
 import 'package:archive/archive_io.dart';
 import 'package:archive/archive_io.dart';
 import 'package:logging/logging.dart';
 import 'package:logging/logging.dart';
+import "package:motion_photos/motion_photos.dart";
 import 'package:motionphoto/motionphoto.dart';
 import 'package:motionphoto/motionphoto.dart';
 import 'package:path/path.dart';
 import 'package:path/path.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:path_provider/path_provider.dart';
@@ -33,6 +34,9 @@ class MediaUploadData {
   final FileHashData? hashData;
   final FileHashData? hashData;
   final int? height;
   final int? height;
   final int? width;
   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(
   MediaUploadData(
     this.sourceFile,
     this.sourceFile,
@@ -41,6 +45,7 @@ class MediaUploadData {
     this.hashData, {
     this.hashData, {
     this.height,
     this.height,
     this.width,
     this.width,
+    this.motionPhotoStartIndex,
   });
   });
 }
 }
 
 
@@ -154,6 +159,15 @@ Future<MediaUploadData> _getMediaUploadDataFromAssetFile(ente.File file) async {
     h = asset.height;
     h = asset.height;
     w = asset.width;
     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(
   return MediaUploadData(
     sourceFile,
     sourceFile,
     thumbnailData,
     thumbnailData,
@@ -161,6 +175,7 @@ Future<MediaUploadData> _getMediaUploadDataFromAssetFile(ente.File file) async {
     FileHashData(fileHash, zipHash: zipHash),
     FileHashData(fileHash, zipHash: zipHash),
     height: h,
     height: h,
     width: w,
     width: w,
+    motionPhotoStartIndex: motionPhotoStartingIndex,
   );
   );
 }
 }
 
 

+ 1 - 1
pubspec.yaml

@@ -12,7 +12,7 @@ description: ente photos application
 # Read more about iOS versioning at
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 
 
-version: 0.7.45+445
+version: 0.7.47+447
 
 
 environment:
 environment:
   sdk: '>=2.17.0 <3.0.0'
   sdk: '>=2.17.0 <3.0.0'