Merge pull request #86 from ente-io/livePhotoBug
Fix bug during first load of remote livePhoto
This commit is contained in:
commit
9c3e73e617
2 changed files with 123 additions and 56 deletions
|
@ -3,6 +3,7 @@ import 'package:chewie/chewie.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
|
@ -13,13 +14,14 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class ZoomableLiveImage extends StatefulWidget {
|
||||
final File photo;
|
||||
final File file;
|
||||
final Function(bool) shouldDisableScroll;
|
||||
final String tagPrefix;
|
||||
final Decoration backgroundDecoration;
|
||||
|
||||
|
||||
ZoomableLiveImage(
|
||||
this.photo, {
|
||||
this.file, {
|
||||
Key key,
|
||||
this.shouldDisableScroll,
|
||||
@required this.tagPrefix,
|
||||
|
@ -33,16 +35,16 @@ class ZoomableLiveImage extends StatefulWidget {
|
|||
class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final Logger _logger = Logger("ZoomableLiveImage");
|
||||
File _livePhoto;
|
||||
bool _loadLivePhotoVideo = false;
|
||||
File _file;
|
||||
bool _showVideo = false;
|
||||
bool _isLoadingVideoPlayer = false;
|
||||
|
||||
VideoPlayerController _videoPlayerController;
|
||||
ChewieController _chewieController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_livePhoto = widget.photo;
|
||||
_loadLiveVideo();
|
||||
_file = widget.file;
|
||||
_showLivePhotoToast();
|
||||
super.initState();
|
||||
}
|
||||
|
@ -54,7 +56,7 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loadLivePhotoVideo = isPressed;
|
||||
_showVideo = isPressed;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -62,10 +64,15 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget content;
|
||||
if (_loadLivePhotoVideo && _videoPlayerController != null) {
|
||||
// check is long press is selected but videoPlayer is not configured yet
|
||||
if (_showVideo && _videoPlayerController == null) {
|
||||
_loadLiveVideo();
|
||||
}
|
||||
|
||||
if (_showVideo && _videoPlayerController != null) {
|
||||
content = _getVideoPlayer();
|
||||
} else {
|
||||
content = ZoomableImage(_livePhoto,
|
||||
content = ZoomableImage(_file,
|
||||
tagPrefix: widget.tagPrefix,
|
||||
shouldDisableScroll: widget.shouldDisableScroll,
|
||||
backgroundDecoration: widget.backgroundDecoration);
|
||||
|
@ -95,29 +102,45 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
aspectRatio: _videoPlayerController.value.aspectRatio,
|
||||
autoPlay: true,
|
||||
autoInitialize: true,
|
||||
looping: false,
|
||||
looping: true,
|
||||
allowFullScreen: false,
|
||||
showControls: false);
|
||||
return Chewie(controller: _chewieController);
|
||||
}
|
||||
|
||||
void _loadLiveVideo() {
|
||||
// todo: add wrapper to download file from server if local is missing
|
||||
getFile(widget.photo, liveVideo: true).then((file) {
|
||||
if (file != null && file.existsSync()) {
|
||||
_logger.fine("loading from local");
|
||||
_setVideoPlayerController(file: file);
|
||||
} else if (widget.photo.uploadedFileID != null) {
|
||||
_logger.fine("loading from remote");
|
||||
getFileFromServer(widget.photo, liveVideo: true).then((file) {
|
||||
if (file != null && file.existsSync()) {
|
||||
_setVideoPlayerController(file: file);
|
||||
} else {
|
||||
_logger.warning("failed to load from remote" + widget.photo.tag());
|
||||
}
|
||||
});
|
||||
}
|
||||
Future<void> _loadLiveVideo() async {
|
||||
// do nothing is already loading or loaded
|
||||
if (_isLoadingVideoPlayer || _videoPlayerController != null) {
|
||||
return;
|
||||
}
|
||||
_isLoadingVideoPlayer = true;
|
||||
if (_file.isRemoteFile() && !(await isFileCached(_file, liveVideo: true))) {
|
||||
showToast("downloading...", toastLength: Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
var videoFile = await getFile(widget.file, liveVideo: true)
|
||||
.timeout(Duration(seconds: 15))
|
||||
.onError((e, s) {
|
||||
_logger.info("getFile failed ${_file.tag()}", e);
|
||||
return null;
|
||||
});
|
||||
|
||||
if ((videoFile == null || !videoFile.existsSync()) &&
|
||||
_file.isRemoteFile()) {
|
||||
videoFile = await getFileFromServer(widget.file, liveVideo: true)
|
||||
.timeout(Duration(seconds: 15))
|
||||
.onError((e, s) {
|
||||
_logger.info("getRemoteFile failed ${_file.tag()}", e);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
if (videoFile != null && videoFile.existsSync()) {
|
||||
_setVideoPlayerController(file: videoFile);
|
||||
} else {
|
||||
showToast("download failed", toastLength: Toast.LENGTH_SHORT);
|
||||
}
|
||||
_isLoadingVideoPlayer = false;
|
||||
}
|
||||
|
||||
VideoPlayerController _setVideoPlayerController({io.File file}) {
|
||||
|
@ -125,7 +148,9 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
return _videoPlayerController = videoPlayerController
|
||||
..initialize().whenComplete(() {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
setState(() {
|
||||
_showVideo = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -98,8 +98,8 @@ void preloadThumbnail(ente.File file) {
|
|||
}
|
||||
}
|
||||
|
||||
final Map<int, Future<io.File>> fileDownloadsInProgress =
|
||||
Map<int, Future<io.File>>();
|
||||
final Map<String, Future<io.File>> fileDownloadsInProgress =
|
||||
<String, Future<io.File>>{};
|
||||
|
||||
Future<io.File> getFileFromServer(
|
||||
ente.File file, {
|
||||
|
@ -109,31 +109,66 @@ Future<io.File> getFileFromServer(
|
|||
final cacheManager = (file.fileType == FileType.video || liveVideo)
|
||||
? VideoCacheManager.instance
|
||||
: DefaultCacheManager();
|
||||
return cacheManager.getFileFromCache(file.getDownloadUrl()).then((info) {
|
||||
if (info == null) {
|
||||
if (!fileDownloadsInProgress.containsKey(file.uploadedFileID)) {
|
||||
if (file.fileType == FileType.livePhoto) {
|
||||
fileDownloadsInProgress[file.uploadedFileID] = _downloadLivePhoto(
|
||||
file,
|
||||
progressCallback: progressCallback,
|
||||
liveVideo: liveVideo);
|
||||
} else {
|
||||
fileDownloadsInProgress[file.uploadedFileID] = _downloadAndCache(
|
||||
file,
|
||||
cacheManager,
|
||||
progressCallback: progressCallback,
|
||||
);
|
||||
}
|
||||
}
|
||||
return fileDownloadsInProgress[file.uploadedFileID];
|
||||
final fileFromCache =
|
||||
await cacheManager.getFileFromCache(file.getDownloadUrl());
|
||||
if (fileFromCache != null) {
|
||||
return fileFromCache.file;
|
||||
}
|
||||
final downloadID = file.uploadedFileID.toString() + liveVideo.toString();
|
||||
if (!fileDownloadsInProgress.containsKey(downloadID)) {
|
||||
if (file.fileType == FileType.livePhoto) {
|
||||
fileDownloadsInProgress[downloadID] = _getLivePhotoFromServer(file,
|
||||
progressCallback: progressCallback, needLiveVideo: liveVideo)
|
||||
.whenComplete(() {
|
||||
fileDownloadsInProgress.remove(downloadID);
|
||||
});
|
||||
} else {
|
||||
return info.file;
|
||||
fileDownloadsInProgress[downloadID] = _downloadAndCache(
|
||||
file, cacheManager,
|
||||
progressCallback: progressCallback)
|
||||
.whenComplete(() {
|
||||
fileDownloadsInProgress.remove(downloadID);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return fileDownloadsInProgress[downloadID];
|
||||
}
|
||||
|
||||
Future<io.File> _downloadLivePhoto(ente.File file,
|
||||
{ProgressCallback progressCallback, bool liveVideo = false}) async {
|
||||
Future<bool> isFileCached(ente.File file,
|
||||
{bool liveVideo = false}) async {
|
||||
final cacheManager = (file.fileType == FileType.video || liveVideo)
|
||||
? VideoCacheManager.instance
|
||||
: DefaultCacheManager();
|
||||
final fileInfo = await cacheManager.getFileFromCache(file.getDownloadUrl());
|
||||
return fileInfo != null;
|
||||
}
|
||||
|
||||
final Map<int, Future<_LivePhoto>> livePhotoDownloadsTracker =
|
||||
<int, Future<_LivePhoto>>{};
|
||||
|
||||
Future<io.File> _getLivePhotoFromServer(ente.File file,
|
||||
{ProgressCallback progressCallback, bool needLiveVideo}) async {
|
||||
final downloadID = file.uploadedFileID;
|
||||
try {
|
||||
if (!livePhotoDownloadsTracker.containsKey(downloadID)) {
|
||||
livePhotoDownloadsTracker[downloadID] =
|
||||
_downloadLivePhoto(file, progressCallback: progressCallback);
|
||||
}
|
||||
final livePhoto = await livePhotoDownloadsTracker[file.uploadedFileID];
|
||||
livePhotoDownloadsTracker.remove(downloadID);
|
||||
if (livePhoto == null) {
|
||||
return null;
|
||||
}
|
||||
return needLiveVideo ? livePhoto.video : livePhoto.image;
|
||||
} catch (e,s) {
|
||||
_logger.warning("live photo get failed", e, s);
|
||||
livePhotoDownloadsTracker.remove(downloadID);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<_LivePhoto> _downloadLivePhoto(ente.File file,
|
||||
{ProgressCallback progressCallback}) async {
|
||||
return downloadAndDecrypt(file, progressCallback: progressCallback)
|
||||
.then((decryptedFile) async {
|
||||
if (decryptedFile == null) {
|
||||
|
@ -189,11 +224,11 @@ Future<io.File> _downloadLivePhoto(ente.File file,
|
|||
}
|
||||
}
|
||||
}
|
||||
fileDownloadsInProgress.remove(file.uploadedFileID);
|
||||
return liveVideo ? videoFileCache : imageFileCache;
|
||||
return _LivePhoto(imageFileCache, videoFileCache);
|
||||
}).catchError((e) {
|
||||
fileDownloadsInProgress.remove(file.uploadedFileID);
|
||||
_logger.warning("failed to download live photos" + e.toString());
|
||||
_logger.warning(
|
||||
"failed to download live photos : ${file.tag()}", e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -224,10 +259,10 @@ Future<io.File> _downloadAndCache(ente.File file, BaseCacheManager cacheManager,
|
|||
fileExtension: fileExtension,
|
||||
);
|
||||
await outputFile.delete();
|
||||
fileDownloadsInProgress.remove(file.uploadedFileID);
|
||||
return cachedFile;
|
||||
}).catchError((e) {
|
||||
fileDownloadsInProgress.remove(file.uploadedFileID);
|
||||
_logger.warning("failed to download file : ${file.tag()}", e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -264,3 +299,10 @@ Future<void> clearCache(ente.File file) async {
|
|||
await cachedThumbnail.delete();
|
||||
}
|
||||
}
|
||||
|
||||
class _LivePhoto {
|
||||
final io.File image;
|
||||
final io.File video;
|
||||
|
||||
_LivePhoto(this.image, this.video);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue