mirror of
https://github.com/immich-app/immich.git
synced 2025-06-03 21:54:21 -04:00
improved motion photo handling
This commit is contained in:
parent
d1c7ed5464
commit
b0a2a6ac13
@ -55,23 +55,12 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final totalAssets = useState(renderList.totalAssets);
|
final totalAssets = useState(renderList.totalAssets);
|
||||||
final isZoomed = useState(false);
|
final isZoomed = useState(false);
|
||||||
final isPlayingMotionVideo = useState(false);
|
final isPlayingMotionVideo = useValueNotifier(false);
|
||||||
final stackIndex = useState(0);
|
final stackIndex = useState(0);
|
||||||
final localPosition = useRef<Offset?>(null);
|
final localPosition = useRef<Offset?>(null);
|
||||||
final currentIndex = useValueNotifier(initialIndex);
|
final currentIndex = useValueNotifier(initialIndex);
|
||||||
final loadAsset = renderList.loadAsset;
|
final loadAsset = renderList.loadAsset;
|
||||||
|
|
||||||
// // Update is playing motion video
|
|
||||||
ref.listen(
|
|
||||||
videoPlaybackValueProvider.select(
|
|
||||||
(playback) => playback.state == VideoPlaybackState.playing,
|
|
||||||
), (_, isPlaying) {
|
|
||||||
final asset = ref.read(currentAssetProvider);
|
|
||||||
if (asset != null && asset.isMotionPhoto) {
|
|
||||||
isPlayingMotionVideo.value = isPlaying;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<void> precacheNextImage(int index) async {
|
Future<void> precacheNextImage(int index) async {
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
return;
|
return;
|
||||||
@ -237,7 +226,8 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
child: NativeVideoViewerPage(
|
child: NativeVideoViewerPage(
|
||||||
key: key,
|
key: key,
|
||||||
asset: asset,
|
asset: asset,
|
||||||
placeholder: Image(
|
isPlayingMotionVideo: isPlayingMotionVideo,
|
||||||
|
image: Image(
|
||||||
key: ValueKey(asset),
|
key: ValueKey(asset),
|
||||||
image: ImmichImage.imageProvider(
|
image: ImmichImage.imageProvider(
|
||||||
asset: asset,
|
asset: asset,
|
||||||
@ -266,7 +256,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newAsset.isImage && !isPlayingMotionVideo.value) {
|
if (newAsset.isImage && !newAsset.isMotionPhoto) {
|
||||||
return buildImage(context, newAsset);
|
return buildImage(context, newAsset);
|
||||||
}
|
}
|
||||||
return buildVideo(context, newAsset);
|
return buildVideo(context, newAsset);
|
||||||
|
@ -24,12 +24,17 @@ import 'package:wakelock_plus/wakelock_plus.dart';
|
|||||||
class NativeVideoViewerPage extends HookConsumerWidget {
|
class NativeVideoViewerPage extends HookConsumerWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
final bool showControls;
|
final bool showControls;
|
||||||
final Widget placeholder;
|
final Widget image;
|
||||||
|
|
||||||
|
/// Whether to display the video part of the motion photo
|
||||||
|
/// TODO: this should probably be a provider
|
||||||
|
final ValueNotifier<bool>? isPlayingMotionVideo;
|
||||||
|
|
||||||
const NativeVideoViewerPage({
|
const NativeVideoViewerPage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.asset,
|
required this.asset,
|
||||||
required this.placeholder,
|
required this.image,
|
||||||
|
this.isPlayingMotionVideo,
|
||||||
this.showControls = true,
|
this.showControls = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -44,6 +49,12 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
final lastVideoPosition = useRef(-1);
|
final lastVideoPosition = useRef(-1);
|
||||||
final isBuffering = useRef(false);
|
final isBuffering = useRef(false);
|
||||||
|
|
||||||
|
if (isPlayingMotionVideo != null) {
|
||||||
|
useListenable(isPlayingMotionVideo);
|
||||||
|
}
|
||||||
|
final showMotionVideo =
|
||||||
|
isPlayingMotionVideo != null && isPlayingMotionVideo!.value;
|
||||||
|
|
||||||
// When a video is opened through the timeline, `isCurrent` will immediately be true.
|
// When a video is opened through the timeline, `isCurrent` will immediately be true.
|
||||||
// When swiping from video A to video B, `isCurrent` will initially be true for video A and false for video B.
|
// When swiping from video A to video B, `isCurrent` will initially be true for video A and false for video B.
|
||||||
// If the swipe is completed, `isCurrent` will be true for video B after a delay.
|
// If the swipe is completed, `isCurrent` will be true for video B after a delay.
|
||||||
@ -413,9 +424,13 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
placeholder, // this is always under the video to avoid flickering
|
// This remains under the video to avoid flickering
|
||||||
|
// For motion videos, this is the image portion of the asset
|
||||||
|
image,
|
||||||
if (aspectRatio.value != null)
|
if (aspectRatio.value != null)
|
||||||
Center(
|
Visibility.maintain(
|
||||||
|
visible: asset.isVideo || showMotionVideo,
|
||||||
|
child: Center(
|
||||||
key: ValueKey(asset),
|
key: ValueKey(asset),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
key: ValueKey(asset),
|
key: ValueKey(asset),
|
||||||
@ -428,6 +443,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
|
|||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
if (showControls) const Center(child: CustomVideoPlayerControls()),
|
if (showControls) const Center(child: CustomVideoPlayerControls()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1086,6 +1086,7 @@ class NativeVideoViewerRoute extends PageRouteInfo<NativeVideoViewerRouteArgs> {
|
|||||||
Key? key,
|
Key? key,
|
||||||
required Asset asset,
|
required Asset asset,
|
||||||
required Widget placeholder,
|
required Widget placeholder,
|
||||||
|
ValueNotifier<bool>? isPlayingMotionVideo,
|
||||||
bool showControls = true,
|
bool showControls = true,
|
||||||
List<PageRouteInfo>? children,
|
List<PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
@ -1094,6 +1095,7 @@ class NativeVideoViewerRoute extends PageRouteInfo<NativeVideoViewerRouteArgs> {
|
|||||||
key: key,
|
key: key,
|
||||||
asset: asset,
|
asset: asset,
|
||||||
placeholder: placeholder,
|
placeholder: placeholder,
|
||||||
|
isPlayingMotionVideo: isPlayingMotionVideo,
|
||||||
showControls: showControls,
|
showControls: showControls,
|
||||||
),
|
),
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
@ -1108,7 +1110,8 @@ class NativeVideoViewerRoute extends PageRouteInfo<NativeVideoViewerRouteArgs> {
|
|||||||
return NativeVideoViewerPage(
|
return NativeVideoViewerPage(
|
||||||
key: args.key,
|
key: args.key,
|
||||||
asset: args.asset,
|
asset: args.asset,
|
||||||
placeholder: args.placeholder,
|
image: args.placeholder,
|
||||||
|
isPlayingMotionVideo: args.isPlayingMotionVideo,
|
||||||
showControls: args.showControls,
|
showControls: args.showControls,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -1120,6 +1123,7 @@ class NativeVideoViewerRouteArgs {
|
|||||||
this.key,
|
this.key,
|
||||||
required this.asset,
|
required this.asset,
|
||||||
required this.placeholder,
|
required this.placeholder,
|
||||||
|
this.isPlayingMotionVideo,
|
||||||
this.showControls = true,
|
this.showControls = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1129,6 +1133,8 @@ class NativeVideoViewerRouteArgs {
|
|||||||
|
|
||||||
final Widget placeholder;
|
final Widget placeholder;
|
||||||
|
|
||||||
|
final ValueNotifier<bool>? isPlayingMotionVideo;
|
||||||
|
|
||||||
final bool showControls;
|
final bool showControls;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -18,7 +18,13 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final assetIsVideo = ref.watch(
|
||||||
|
currentAssetProvider.select((asset) => asset != null && asset.isVideo),
|
||||||
|
);
|
||||||
final showControls = ref.watch(showControlsProvider);
|
final showControls = ref.watch(showControlsProvider);
|
||||||
|
final VideoPlaybackState state =
|
||||||
|
ref.watch(videoPlaybackValueProvider.select((value) => value.state));
|
||||||
|
|
||||||
// A timer to hide the controls
|
// A timer to hide the controls
|
||||||
final hideTimer = useTimer(
|
final hideTimer = useTimer(
|
||||||
hideTimerDuration,
|
hideTimerDuration,
|
||||||
@ -27,20 +33,12 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final state = ref.read(videoPlaybackValueProvider).state;
|
|
||||||
// Do not hide on paused
|
// Do not hide on paused
|
||||||
if (state == VideoPlaybackState.paused) {
|
if (state != VideoPlaybackState.paused && assetIsVideo) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final asset = ref.read(currentAssetProvider);
|
|
||||||
if (asset != null && asset.isVideo) {
|
|
||||||
ref.read(showControlsProvider.notifier).show = false;
|
ref.read(showControlsProvider.notifier).show = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final VideoPlaybackState state =
|
|
||||||
ref.watch(videoPlaybackValueProvider.select((value) => value.state));
|
|
||||||
final showBuffering = state == VideoPlaybackState.buffering;
|
final showBuffering = state == VideoPlaybackState.buffering;
|
||||||
|
|
||||||
/// Shows the controls and starts the timer to hide them
|
/// Shows the controls and starts the timer to hide them
|
||||||
@ -95,7 +93,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget {
|
|||||||
iconColor: Colors.white,
|
iconColor: Colors.white,
|
||||||
isFinished: state == VideoPlaybackState.completed,
|
isFinished: state == VideoPlaybackState.completed,
|
||||||
isPlaying: state == VideoPlaybackState.playing,
|
isPlaying: state == VideoPlaybackState.playing,
|
||||||
show: showControls,
|
show: assetIsVideo && showControls,
|
||||||
onPressed: togglePlay,
|
onPressed: togglePlay,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -72,7 +72,7 @@ class MemoryCard extends StatelessWidget {
|
|||||||
key: ValueKey(asset.id),
|
key: ValueKey(asset.id),
|
||||||
asset: asset,
|
asset: asset,
|
||||||
showControls: false,
|
showControls: false,
|
||||||
placeholder: SizedBox.expand(
|
image: SizedBox.expand(
|
||||||
child: ImmichImage(
|
child: ImmichImage(
|
||||||
asset,
|
asset,
|
||||||
fit: fit,
|
fit: fit,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user