1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-28 22:51:59 +00:00

Videos play in memories now

This commit is contained in:
Marty Fuhry 2024-02-04 16:15:19 -05:00
parent b0ff859cd6
commit f14295e107
No known key found for this signature in database
GPG key ID: E2AB6392D894D900
3 changed files with 108 additions and 55 deletions

View file

@ -21,40 +21,45 @@ class VideoViewerPage extends HookConsumerWidget {
final Asset asset;
final bool isMotionVideo;
final Widget? placeholder;
final VoidCallback onVideoEnded;
final VoidCallback? onVideoEnded;
final VoidCallback? onPlaying;
final VoidCallback? onPaused;
final Duration hideControlsTimer;
final bool showControls;
final bool showDownloadingIndicator;
const VideoViewerPage({
super.key,
required this.asset,
required this.isMotionVideo,
required this.onVideoEnded,
this.isMotionVideo = false,
this.onVideoEnded,
this.onPlaying,
this.onPaused,
this.placeholder,
this.showControls = true,
this.hideControlsTimer = const Duration(seconds: 5),
this.showDownloadingIndicator = true,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
if (asset.isLocal && asset.livePhotoVideoId == null) {
final AsyncValue<File> videoFile = ref.watch(_fileFamily(asset.local!));
return videoFile.when(
data: (data) => VideoPlayer(
file: data,
isMotionVideo: false,
onVideoEnded: () {},
),
error: (error, stackTrace) => Icon(
Icons.image_not_supported_outlined,
color: context.primaryColor,
),
loading: () => const Center(
child: SizedBox(
width: 75,
height: 75,
child: CircularProgressIndicator.adaptive(),
return AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: videoFile.when(
data: (data) => VideoPlayer(
file: data,
isMotionVideo: false,
onVideoEnded: () {},
),
error: (error, stackTrace) => Icon(
Icons.image_not_supported_outlined,
color: context.primaryColor,
),
loading: () => showDownloadingIndicator
? const Center(child: ImmichLoadingIndicator())
: Container(),
),
);
}
@ -74,15 +79,24 @@ class VideoViewerPage extends HookConsumerWidget {
onPaused: onPaused,
onPlaying: onPlaying,
placeholder: placeholder,
hideControlsTimer: hideControlsTimer,
showControls: showControls,
showDownloadingIndicator: showDownloadingIndicator,
),
if (downloadAssetStatus == DownloadAssetStatus.loading)
SizedBox(
AnimatedOpacity(
duration: const Duration(milliseconds: 400),
opacity: (downloadAssetStatus == DownloadAssetStatus.loading &&
showDownloadingIndicator)
? 1.0
: 0.0,
child: SizedBox(
height: context.height,
width: context.width,
child: const Center(
child: ImmichLoadingIndicator(),
),
),
),
],
);
}
@ -102,7 +116,9 @@ class VideoPlayer extends StatefulWidget {
final String? jwtToken;
final File? file;
final bool isMotionVideo;
final VoidCallback onVideoEnded;
final VoidCallback? onVideoEnded;
final Duration hideControlsTimer;
final bool showControls;
final Function()? onPlaying;
final Function()? onPaused;
@ -111,16 +127,23 @@ class VideoPlayer extends StatefulWidget {
/// usually, a thumbnail of the video
final Widget? placeholder;
final bool showDownloadingIndicator;
const VideoPlayer({
super.key,
this.url,
this.jwtToken,
this.file,
required this.onVideoEnded,
this.onVideoEnded,
required this.isMotionVideo,
this.onPlaying,
this.onPaused,
this.placeholder,
this.hideControlsTimer = const Duration(
seconds: 5,
),
this.showControls = true,
this.showDownloadingIndicator = true,
});
@override
@ -149,7 +172,7 @@ class _VideoPlayerState extends State<VideoPlayer> {
if (videoPlayerController.value.position ==
videoPlayerController.value.duration) {
WakelockPlus.disable();
widget.onVideoEnded();
widget.onVideoEnded?.call();
}
}
});
@ -184,9 +207,9 @@ class _VideoPlayerState extends State<VideoPlayer> {
autoInitialize: true,
allowFullScreen: false,
allowedScreenSleep: false,
showControls: !widget.isMotionVideo,
showControls: widget.showControls && !widget.isMotionVideo,
customControls: const VideoPlayerControls(),
hideControlsTimer: const Duration(seconds: 5),
hideControlsTimer: widget.hideControlsTimer,
);
}
@ -214,9 +237,10 @@ class _VideoPlayerState extends State<VideoPlayer> {
child: Stack(
children: [
if (widget.placeholder != null) widget.placeholder!,
const Center(
child: ImmichLoadingIndicator(),
),
if (widget.showDownloadingIndicator)
const Center(
child: ImmichLoadingIndicator(),
),
],
),
),

View file

@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart';
@ -11,15 +12,15 @@ import 'package:openapi/api.dart';
class MemoryCard extends StatelessWidget {
final Asset asset;
final void Function() onTap;
final String title;
final bool showTitle;
final Function()? onVideoEnded;
const MemoryCard({
required this.asset,
required this.onTap,
required this.title,
required this.showTitle,
this.onVideoEnded,
super.key,
});
@ -59,24 +60,23 @@ class MemoryCard extends StatelessWidget {
child: Container(color: Colors.black.withOpacity(0.2)),
),
),
GestureDetector(
onTap: onTap,
child: LayoutBuilder(
builder: (context, constraints) {
// Determine the fit using the aspect ratio
BoxFit fit = BoxFit.fitWidth;
if (asset.width != null && asset.height != null) {
final aspectRatio = asset.width! / asset.height!;
final phoneAspectRatio =
constraints.maxWidth / constraints.maxHeight;
// Look for a 25% difference in either direction
if (phoneAspectRatio * .75 < aspectRatio &&
phoneAspectRatio * 1.25 > aspectRatio) {
// Cover to look nice if we have nearly the same aspect ratio
fit = BoxFit.cover;
}
LayoutBuilder(
builder: (context, constraints) {
// Determine the fit using the aspect ratio
BoxFit fit = BoxFit.fitWidth;
if (asset.width != null && asset.height != null) {
final aspectRatio = asset.height! / asset.width!;
final phoneAspectRatio =
constraints.maxWidth / constraints.maxHeight;
// Look for a 25% difference in either direction
if (phoneAspectRatio * .75 < aspectRatio &&
phoneAspectRatio * 1.25 > aspectRatio) {
// Cover to look nice if we have nearly the same aspect ratio
fit = BoxFit.cover;
}
}
if (asset.isImage) {
return Hero(
tag: 'memory-${asset.id}',
child: ImmichImage(
@ -88,8 +88,25 @@ class MemoryCard extends StatelessWidget {
preferredLocalAssetSize: 2048,
),
);
},
),
} else {
return Hero(
tag: 'memory-${asset.id}',
child: VideoViewerPage(
asset: asset,
showDownloadingIndicator: false,
placeholder: ImmichImage(
asset,
fit: fit,
type: ThumbnailFormat.JPEG,
preferredLocalAssetSize: 2048,
),
hideControlsTimer: const Duration(seconds: 2),
onVideoEnded: onVideoEnded,
showControls: false,
),
);
}
},
),
if (showTitle)
Positioned(

View file

@ -245,13 +245,25 @@ class MemoryPage extends HookConsumerWidget {
itemCount: memories[mIndex].assets.length,
itemBuilder: (context, index) {
final asset = memories[mIndex].assets[index];
return Container(
color: Colors.black,
child: MemoryCard(
asset: asset,
onTap: () => toNextAsset(index),
title: memories[mIndex].title,
showTitle: index == 0,
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
toNextAsset(index);
},
child: Container(
color: Colors.black,
child: MemoryCard(
asset: asset,
title: memories[mIndex].title,
showTitle: index == 0,
onVideoEnded: () {
// If this is a live photo, don't go to
// next asset
if (asset.livePhotoVideoId == null) {
toNextAsset(index);
}
},
),
),
);
},