diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d915141a6..77ac42cf1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -32,6 +32,18 @@ + + + + + + + + + + + + diff --git a/lib/ui/video_widget.dart b/lib/ui/video_widget.dart index 0970eec31..c7dcaec17 100644 --- a/lib/ui/video_widget.dart +++ b/lib/ui/video_widget.dart @@ -43,6 +43,14 @@ class _VideoWidgetState extends State { super.initState(); if (widget.file.isRemoteFile()) { _loadNetworkVideo(); + } else if (widget.file.isSharedMediaToAppSandbox()) { + final localFile = io.File(getSharedMediaFilePath(widget.file)); + if (localFile.existsSync()) { + _logger.fine("loading from app cache"); + _setVideoPlayerController(file: localFile); + } else if (widget.file.uploadedFileID != null) { + _loadNetworkVideo(); + } } else { widget.file.getAsset().then((asset) async { if (asset == null || !(await asset.exists)) { @@ -62,12 +70,14 @@ class _VideoWidgetState extends State { getFileFromServer( widget.file, progressCallback: (count, total) { - setState(() { - _progress = count / total; - if (_progress == 1) { - showToast("decrypting video...", toastLength: Toast.LENGTH_SHORT); - } - }); + if (mounted) { + setState(() { + _progress = count / total; + if (_progress == 1) { + showToast("decrypting video...", toastLength: Toast.LENGTH_SHORT); + } + }); + } }, ).then((file) { if (file != null) { diff --git a/lib/utils/file_uploader_util.dart b/lib/utils/file_uploader_util.dart index 77b9c111c..8a5ed73da 100644 --- a/lib/utils/file_uploader_util.dart +++ b/lib/utils/file_uploader_util.dart @@ -6,6 +6,7 @@ import 'package:archive/archive_io.dart'; import 'package:logging/logging.dart'; import 'package:motionphoto/motionphoto.dart'; import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:photos/core/configuration.dart'; import 'package:photos/core/constants.dart'; @@ -13,6 +14,7 @@ import 'package:photos/core/errors.dart'; import 'package:photos/models/file.dart' as ente; import 'package:photos/models/file_type.dart'; import 'package:photos/models/location.dart'; +import 'package:video_thumbnail/video_thumbnail.dart'; import 'file_util.dart'; @@ -140,8 +142,14 @@ Future _getMediaUploadDataFromAppCache(ente.File file) async { _logger.warning("File doesn't exist in app sandbox"); throw InvalidFileError("File doesn't exist in app sandbox"); } - thumbnailData = await getThumbnailFromInAppCacheFile(file); - return MediaUploadData(sourceFile, thumbnailData, isDeleted); + try { + thumbnailData = await getThumbnailFromInAppCacheFile(file); + return MediaUploadData(sourceFile, thumbnailData, isDeleted); + } catch (e, s) { + _logger.severe("failed to generate thumbnail", e, s); + throw InvalidFileError( + "thumbnail generation failed for fileType: ${file.fileType.toString()}"); + } } Future getThumbnailFromInAppCacheFile(ente.File file) async { @@ -149,6 +157,16 @@ Future getThumbnailFromInAppCacheFile(ente.File file) async { if (!localFile.existsSync()) { return null; } + if (file.fileType == FileType.video) { + final thumbnailFilePath = await VideoThumbnail.thumbnailFile( + video: localFile.path, + imageFormat: ImageFormat.JPEG, + thumbnailPath: (await getTemporaryDirectory()).path, + maxWidth: kThumbnailLargeSize, + quality: 80, + ); + localFile = io.File(thumbnailFilePath); + } var thumbnailData = await localFile.readAsBytes(); int compressionAttempts = 0; while (thumbnailData.length > kThumbnailDataLimit && diff --git a/lib/utils/share_util.dart b/lib/utils/share_util.dart index 33bfdd5bc..2bef1c831 100644 --- a/lib/utils/share_util.dart +++ b/lib/utils/share_util.dart @@ -40,29 +40,40 @@ Future> convertIncomingSharedMediaToFile( List sharedMedia, int collectionID) async { List localFiles = []; for (var media in sharedMedia) { + if (!(media.type == SharedMediaType.IMAGE || + media.type == SharedMediaType.VIDEO)) { + _logger.warning( + "ignore unsupported file type ${media.type.toString()} path: ${media.path}"); + continue; + } var enteFile = File(); // fileName: img_x.jpg enteFile.title = basename(media.path); - var ioFile = dartio.File(media.path); - ioFile = ioFile.renameSync(Configuration.instance.getSharedMediaCacheDirectory() + - "/" + - enteFile.title); + ioFile = ioFile.renameSync( + Configuration.instance.getSharedMediaCacheDirectory() + + "/" + + enteFile.title); enteFile.localID = kSharedMediaIdentifier + enteFile.title; enteFile.collectionID = collectionID; - enteFile.fileType = FileType.image; + enteFile.fileType = + media.type == SharedMediaType.IMAGE ? FileType.image : FileType.video; - var exifMap = await readExifFromFile(ioFile); - if (exifMap != null && - exifMap["Image DateTime"] != null && - '0000:00:00 00:00:00' != exifMap["Image DateTime"].toString()) { - try { - final exifTime = - _exifDateFormat.parse(exifMap["Image DateTime"].toString()); - enteFile.creationTime = exifTime.microsecondsSinceEpoch; - } catch (e) { - //ignore + if (enteFile.fileType == FileType.image) { + final exifMap = await readExifFromFile(ioFile); + if (exifMap != null && + exifMap["Image DateTime"] != null && + '0000:00:00 00:00:00' != exifMap["Image DateTime"].toString()) { + try { + final exifTime = + _exifDateFormat.parse(exifMap["Image DateTime"].toString()); + enteFile.creationTime = exifTime.microsecondsSinceEpoch; + } catch (e) { + //ignore + } } + } else if (enteFile.fileType == FileType.video) { + enteFile.duration = media.duration ?? 0; } if (enteFile.creationTime == null || enteFile.creationTime == 0) { final parsedDateTime = diff --git a/pubspec.lock b/pubspec.lock index 81190bbe3..5325d50d6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1091,6 +1091,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + video_thumbnail: + dependency: "direct main" + description: + name: video_thumbnail + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3" visibility_detector: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b98ee16b2..b48b0d62d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -98,6 +98,7 @@ dependencies: url_launcher: ^6.0.3 uuid: ^3.0.4 video_player: ^2.0.0 + video_thumbnail: ^0.4.3 visibility_detector: ^0.2.0 wallpaper_manager_flutter: ^0.0.2