2024-01-15 18:50:33 +02:00
|
|
|
import 'package:auto_route/auto_route.dart';
|
2022-02-06 08:07:56 +02:00
|
|
|
import 'package:flutter/material.dart';
|
2024-02-23 07:18:02 +02:00
|
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
2024-03-06 05:42:22 +02:00
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart';
|
|
|
|
import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controller_provider.dart';
|
|
|
|
import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart';
|
|
|
|
import 'package:immich_mobile/modules/asset_viewer/providers/video_player_value_provider.dart';
|
|
|
|
import 'package:immich_mobile/modules/asset_viewer/ui/video_player.dart';
|
2022-11-08 19:00:24 +02:00
|
|
|
import 'package:immich_mobile/shared/models/asset.dart';
|
2024-02-23 07:18:02 +02:00
|
|
|
import 'package:immich_mobile/shared/ui/delayed_loading_indicator.dart';
|
2024-03-06 05:42:22 +02:00
|
|
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
2022-02-06 08:07:56 +02:00
|
|
|
|
2024-01-15 18:50:33 +02:00
|
|
|
@RoutePage()
|
2022-04-02 19:31:53 +02:00
|
|
|
// ignore: must_be_immutable
|
2024-03-06 05:42:22 +02:00
|
|
|
class VideoViewerPage extends HookConsumerWidget {
|
2022-11-08 19:00:24 +02:00
|
|
|
final Asset asset;
|
2022-11-19 07:12:54 +02:00
|
|
|
final bool isMotionVideo;
|
2023-06-20 23:17:43 +02:00
|
|
|
final Widget? placeholder;
|
2024-02-08 06:07:50 +02:00
|
|
|
final Duration hideControlsTimer;
|
|
|
|
final bool showControls;
|
|
|
|
final bool showDownloadingIndicator;
|
2022-02-06 08:07:56 +02:00
|
|
|
|
2022-11-19 07:12:54 +02:00
|
|
|
const VideoViewerPage({
|
2024-01-27 18:14:32 +02:00
|
|
|
super.key,
|
2022-11-19 07:12:54 +02:00
|
|
|
required this.asset,
|
2024-02-08 06:07:50 +02:00
|
|
|
this.isMotionVideo = false,
|
2023-06-20 23:17:43 +02:00
|
|
|
this.placeholder,
|
2024-02-08 06:07:50 +02:00
|
|
|
this.showControls = true,
|
|
|
|
this.hideControlsTimer = const Duration(seconds: 5),
|
|
|
|
this.showDownloadingIndicator = true,
|
2024-01-27 18:14:32 +02:00
|
|
|
});
|
2022-02-06 08:07:56 +02:00
|
|
|
|
|
|
|
@override
|
2024-03-06 05:42:22 +02:00
|
|
|
build(BuildContext context, WidgetRef ref) {
|
|
|
|
final controller =
|
|
|
|
ref.watch(videoPlayerControllerProvider(asset: asset)).value;
|
|
|
|
// The last volume of the video used when mute is toggled
|
|
|
|
final lastVolume = useState(0.5);
|
|
|
|
|
|
|
|
// When the volume changes, set the volume
|
|
|
|
ref.listen(videoPlayerControlsProvider.select((value) => value.mute),
|
|
|
|
(_, mute) {
|
|
|
|
if (mute) {
|
|
|
|
controller?.setVolume(0.0);
|
|
|
|
} else {
|
|
|
|
controller?.setVolume(lastVolume.value);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// When the position changes, seek to the position
|
|
|
|
ref.listen(videoPlayerControlsProvider.select((value) => value.position),
|
|
|
|
(_, position) {
|
|
|
|
if (controller == null) {
|
|
|
|
// No seeeking if there is no video
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the position to seek to
|
|
|
|
final Duration seek = controller.value.duration * (position / 100.0);
|
|
|
|
controller.seekTo(seek);
|
|
|
|
});
|
|
|
|
|
|
|
|
// When the custom video controls paus or plays
|
|
|
|
ref.listen(videoPlayerControlsProvider.select((value) => value.pause),
|
|
|
|
(lastPause, pause) {
|
|
|
|
if (pause) {
|
|
|
|
controller?.pause();
|
|
|
|
} else {
|
|
|
|
controller?.play();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Updates the [videoPlaybackValueProvider] with the current
|
|
|
|
// position and duration of the video from the Chewie [controller]
|
|
|
|
// Also sets the error if there is an error in the playback
|
|
|
|
void updateVideoPlayback() {
|
|
|
|
final videoPlayback = VideoPlaybackValue.fromController(controller);
|
|
|
|
ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback;
|
|
|
|
final state = videoPlayback.state;
|
|
|
|
|
|
|
|
// Enable the WakeLock while the video is playing
|
|
|
|
if (state == VideoPlaybackState.playing) {
|
|
|
|
// Sync with the controls playing
|
|
|
|
WakelockPlus.enable();
|
|
|
|
} else {
|
|
|
|
// Sync with the controls pause
|
|
|
|
WakelockPlus.disable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Adds and removes the listener to the video player
|
|
|
|
useEffect(
|
|
|
|
() {
|
|
|
|
Future.microtask(
|
|
|
|
() => ref.read(videoPlayerControlsProvider.notifier).reset(),
|
|
|
|
);
|
|
|
|
// Guard no controller
|
|
|
|
if (controller == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hide the controls
|
|
|
|
// Done in a microtask to avoid setting the state while the is building
|
|
|
|
if (!isMotionVideo) {
|
|
|
|
Future.microtask(() {
|
|
|
|
ref.read(showControlsProvider.notifier).show = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subscribes to listener
|
|
|
|
controller.addListener(updateVideoPlayback);
|
|
|
|
return () {
|
|
|
|
// Removes listener when we dispose
|
|
|
|
controller.removeListener(updateVideoPlayback);
|
|
|
|
controller.pause();
|
|
|
|
};
|
|
|
|
},
|
|
|
|
[controller],
|
2022-02-06 08:07:56 +02:00
|
|
|
);
|
|
|
|
|
2024-03-06 05:42:22 +02:00
|
|
|
final size = MediaQuery.sizeOf(context);
|
|
|
|
|
2024-02-23 07:18:02 +02:00
|
|
|
return PopScope(
|
2024-03-06 05:42:22 +02:00
|
|
|
onPopInvoked: (pop) {
|
|
|
|
ref.read(videoPlaybackValueProvider.notifier).value =
|
|
|
|
VideoPlaybackValue.uninitialized();
|
|
|
|
},
|
2024-02-23 07:18:02 +02:00
|
|
|
child: AnimatedSwitcher(
|
|
|
|
duration: const Duration(milliseconds: 400),
|
2024-03-06 05:42:22 +02:00
|
|
|
child: Stack(
|
|
|
|
children: [
|
|
|
|
Visibility(
|
|
|
|
visible: controller == null,
|
|
|
|
child: Stack(
|
2024-02-23 07:18:02 +02:00
|
|
|
children: [
|
2024-02-28 23:48:59 +02:00
|
|
|
if (placeholder != null) placeholder!,
|
|
|
|
const Positioned.fill(
|
|
|
|
child: Center(
|
|
|
|
child: DelayedLoadingIndicator(
|
|
|
|
fadeInDuration: Duration(milliseconds: 500),
|
|
|
|
),
|
|
|
|
),
|
2024-02-23 07:18:02 +02:00
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
2024-03-06 05:42:22 +02:00
|
|
|
),
|
|
|
|
if (controller != null)
|
|
|
|
SizedBox(
|
|
|
|
height: size.height,
|
|
|
|
width: size.width,
|
|
|
|
child: VideoPlayerViewer(
|
|
|
|
controller: controller,
|
|
|
|
isMotionVideo: isMotionVideo,
|
|
|
|
placeholder: placeholder,
|
|
|
|
hideControlsTimer: hideControlsTimer,
|
|
|
|
showControls: showControls,
|
|
|
|
showDownloadingIndicator: showDownloadingIndicator,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
2023-04-18 18:23:56 +02:00
|
|
|
),
|
2024-02-23 07:18:02 +02:00
|
|
|
),
|
|
|
|
);
|
2022-02-06 08:07:56 +02:00
|
|
|
}
|
|
|
|
}
|