mirror of
https://github.com/immich-app/immich.git
synced 2025-03-11 02:23:09 -05:00
fix(mobile): video player disposes early (#2275)
* fix(mobile): video player disposes early * fixed show download button based on asset state * style icon size * disable screensleep on video player * better position for video * better scroll physics on iOS
This commit is contained in:
parent
8a45c258c5
commit
c8d3faec6d
4 changed files with 53 additions and 35 deletions
|
@ -27,7 +27,7 @@ class TopControlAppBar extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
const double iconSize = 18.0;
|
const double iconSize = 22.0;
|
||||||
|
|
||||||
Widget buildFavoriteButton() {
|
Widget buildFavoriteButton() {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
@ -82,6 +82,14 @@ class TopControlAppBar extends HookConsumerWidget {
|
||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (asset.storage == AssetState.merged)
|
||||||
|
IconButton(
|
||||||
|
onPressed: onDownloadPressed,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.cloud_download_outlined,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
),
|
||||||
|
),
|
||||||
if (asset.isRemote)
|
if (asset.isRemote)
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|
|
@ -301,7 +301,8 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
onFavorite: () {
|
onFavorite: () {
|
||||||
toggleFavorite(assetList[indexOfAsset.value]);
|
toggleFavorite(assetList[indexOfAsset.value]);
|
||||||
},
|
},
|
||||||
onDownloadPressed: assetList[indexOfAsset.value].isLocal
|
onDownloadPressed: assetList[indexOfAsset.value].storage ==
|
||||||
|
AssetState.local
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
ref.watch(imageViewerStateProvider.notifier).downloadAsset(
|
ref.watch(imageViewerStateProvider.notifier).downloadAsset(
|
||||||
|
@ -391,7 +392,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
scrollPhysics: isZoomed.value
|
scrollPhysics: isZoomed.value
|
||||||
? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in
|
? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in
|
||||||
: (Platform.isIOS
|
: (Platform.isIOS
|
||||||
? const BouncingScrollPhysics() // Use bouncing physics for iOS
|
? const ScrollPhysics() // Use bouncing physics for iOS
|
||||||
: const ClampingScrollPhysics() // Use heavy physics for Android
|
: const ClampingScrollPhysics() // Use heavy physics for Android
|
||||||
),
|
),
|
||||||
itemCount: assetList.length,
|
itemCount: assetList.length,
|
||||||
|
@ -516,6 +517,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
maxScale: 1.0,
|
maxScale: 1.0,
|
||||||
minScale: 1.0,
|
minScale: 1.0,
|
||||||
|
basePosition: Alignment.bottomCenter,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: VideoViewerPage(
|
child: VideoViewerPage(
|
||||||
onPlaying: () => isPlayingVideo.value = true,
|
onPlaying: () => isPlayingVideo.value = true,
|
||||||
|
|
|
@ -30,10 +30,10 @@ class VideoViewerPage extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
if (asset.isLocal) {
|
if (asset.storage == AssetState.local && asset.livePhotoVideoId == null) {
|
||||||
final AsyncValue<File> videoFile = ref.watch(_fileFamily(asset.local!));
|
final AsyncValue<File> videoFile = ref.watch(_fileFamily(asset.local!));
|
||||||
return videoFile.when(
|
return videoFile.when(
|
||||||
data: (data) => VideoThumbnailPlayer(
|
data: (data) => VideoPlayer(
|
||||||
file: data,
|
file: data,
|
||||||
isMotionVideo: false,
|
isMotionVideo: false,
|
||||||
onVideoEnded: () {},
|
onVideoEnded: () {},
|
||||||
|
@ -59,7 +59,7 @@ class VideoViewerPage extends HookConsumerWidget {
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
VideoThumbnailPlayer(
|
VideoPlayer(
|
||||||
url: videoUrl,
|
url: videoUrl,
|
||||||
jwtToken: Store.get(StoreKey.accessToken),
|
jwtToken: Store.get(StoreKey.accessToken),
|
||||||
isMotionVideo: isMotionVideo,
|
isMotionVideo: isMotionVideo,
|
||||||
|
@ -85,7 +85,7 @@ final _fileFamily =
|
||||||
return file;
|
return file;
|
||||||
});
|
});
|
||||||
|
|
||||||
class VideoThumbnailPlayer extends StatefulWidget {
|
class VideoPlayer extends StatefulWidget {
|
||||||
final String? url;
|
final String? url;
|
||||||
final String? jwtToken;
|
final String? jwtToken;
|
||||||
final File? file;
|
final File? file;
|
||||||
|
@ -95,7 +95,7 @@ class VideoThumbnailPlayer extends StatefulWidget {
|
||||||
final Function()? onPlaying;
|
final Function()? onPlaying;
|
||||||
final Function()? onPaused;
|
final Function()? onPaused;
|
||||||
|
|
||||||
const VideoThumbnailPlayer({
|
const VideoPlayer({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.url,
|
this.url,
|
||||||
this.jwtToken,
|
this.jwtToken,
|
||||||
|
@ -107,10 +107,10 @@ class VideoThumbnailPlayer extends StatefulWidget {
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<VideoThumbnailPlayer> createState() => _VideoThumbnailPlayerState();
|
State<VideoPlayer> createState() => _VideoPlayerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
class _VideoPlayerState extends State<VideoPlayer> {
|
||||||
late VideoPlayerController videoPlayerController;
|
late VideoPlayerController videoPlayerController;
|
||||||
ChewieController? chewieController;
|
ChewieController? chewieController;
|
||||||
|
|
||||||
|
@ -120,15 +120,18 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
||||||
initializePlayer();
|
initializePlayer();
|
||||||
|
|
||||||
videoPlayerController.addListener(() {
|
videoPlayerController.addListener(() {
|
||||||
|
if (videoPlayerController.value.isInitialized) {
|
||||||
if (videoPlayerController.value.isPlaying) {
|
if (videoPlayerController.value.isPlaying) {
|
||||||
widget.onPlaying?.call();
|
widget.onPlaying?.call();
|
||||||
} else if (!videoPlayerController.value.isPlaying) {
|
} else if (!videoPlayerController.value.isPlaying) {
|
||||||
widget.onPaused?.call();
|
widget.onPaused?.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoPlayerController.value.position ==
|
if (videoPlayerController.value.position ==
|
||||||
videoPlayerController.value.duration) {
|
videoPlayerController.value.duration) {
|
||||||
widget.onVideoEnded();
|
widget.onVideoEnded();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,14 +148,14 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
||||||
_createChewieController();
|
_createChewieController();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("ERROR initialize video player");
|
debugPrint("ERROR initialize video player $e");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_createChewieController() {
|
_createChewieController() {
|
||||||
chewieController = ChewieController(
|
chewieController = ChewieController(
|
||||||
controlsSafeAreaMinimum: const EdgeInsets.only(
|
controlsSafeAreaMinimum: const EdgeInsets.only(
|
||||||
bottom: 156,
|
bottom: 100,
|
||||||
),
|
),
|
||||||
showOptions: true,
|
showOptions: true,
|
||||||
showControlsOnInitialize: false,
|
showControlsOnInitialize: false,
|
||||||
|
@ -160,6 +163,7 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
||||||
autoPlay: true,
|
autoPlay: true,
|
||||||
autoInitialize: true,
|
autoInitialize: true,
|
||||||
allowFullScreen: true,
|
allowFullScreen: true,
|
||||||
|
allowedScreenSleep: false,
|
||||||
showControls: !widget.isMotionVideo,
|
showControls: !widget.isMotionVideo,
|
||||||
hideControlsTimer: const Duration(seconds: 5),
|
hideControlsTimer: const Duration(seconds: 5),
|
||||||
);
|
);
|
||||||
|
@ -175,13 +179,14 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return chewieController?.videoPlayerController.value.isInitialized == true
|
if (chewieController?.videoPlayerController.value.isInitialized == true) {
|
||||||
? SizedBox(
|
return SizedBox(
|
||||||
child: Chewie(
|
child: Chewie(
|
||||||
controller: chewieController!,
|
controller: chewieController!,
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
: const Center(
|
} else {
|
||||||
|
return const Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 75,
|
width: 75,
|
||||||
height: 75,
|
height: 75,
|
||||||
|
@ -192,3 +197,4 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -371,6 +371,8 @@ class Asset {
|
||||||
"fileName": "$fileName",
|
"fileName": "$fileName",
|
||||||
"isFavorite": $isFavorite,
|
"isFavorite": $isFavorite,
|
||||||
"isLocal": $isLocal,
|
"isLocal": $isLocal,
|
||||||
|
"isRemote: $isRemote,
|
||||||
|
"storage": $storage,
|
||||||
"width": ${width ?? "N/A"},
|
"width": ${width ?? "N/A"},
|
||||||
"height": ${height ?? "N/A"},
|
"height": ${height ?? "N/A"},
|
||||||
"isArchived": $isArchived
|
"isArchived": $isArchived
|
||||||
|
|
Loading…
Add table
Reference in a new issue