splitup the player

This commit is contained in:
Alex 2024-08-09 17:30:30 -05:00 committed by shenlong-tanwen
parent e0ed1fcecc
commit c5cc4930fb
6 changed files with 240 additions and 80 deletions

View File

@ -10,6 +10,7 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/pages/common/native_video_viewer.page.dart';
import 'package:immich_mobile/pages/common/video_viewer.page.dart'; import 'package:immich_mobile/pages/common/video_viewer.page.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_stack.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_stack.provider.dart';
@ -61,7 +62,6 @@ class GalleryViewerPage extends HookConsumerWidget {
final localPosition = useState<Offset?>(null); final localPosition = useState<Offset?>(null);
final currentIndex = useState(initialIndex); final currentIndex = useState(initialIndex);
final currentAsset = loadAsset(currentIndex.value); final currentAsset = loadAsset(currentIndex.value);
// Update is playing motion video // Update is playing motion video
ref.listen(videoPlaybackValueProvider.select((v) => v.state), (_, state) { ref.listen(videoPlaybackValueProvider.select((v) => v.state), (_, state) {
isPlayingVideo.value = state == VideoPlaybackState.playing; isPlayingVideo.value = state == VideoPlaybackState.playing;
@ -352,6 +352,9 @@ class GalleryViewerPage extends HookConsumerWidget {
), ),
); );
} else { } else {
final useNativePlayer =
asset.isLocal && asset.livePhotoVideoId == null;
return PhotoViewGalleryPageOptions.customChild( return PhotoViewGalleryPageOptions.customChild(
onDragStart: (_, details, __) => onDragStart: (_, details, __) =>
localPosition.value = details.localPosition, localPosition.value = details.localPosition,
@ -366,19 +369,32 @@ class GalleryViewerPage extends HookConsumerWidget {
maxScale: 1.0, maxScale: 1.0,
minScale: 1.0, minScale: 1.0,
basePosition: Alignment.center, basePosition: Alignment.center,
child: VideoViewerPage( child: useNativePlayer
key: ValueKey(a), ? NativeVideoViewerPage(
asset: a, key: ValueKey(a),
isMotionVideo: a.livePhotoVideoId != null, asset: a,
loopVideo: shouldLoopVideo.value, // loopVideo: shouldLoopVideo.value,
placeholder: Image( // placeholder: Image(
image: provider, // image: provider,
fit: BoxFit.contain, // fit: BoxFit.contain,
height: context.height, // height: context.height,
width: context.width, // width: context.width,
alignment: Alignment.center, // alignment: Alignment.center,
), // ),
), )
: VideoViewerPage(
key: ValueKey(a),
asset: a,
isMotionVideo: a.livePhotoVideoId != null,
loopVideo: shouldLoopVideo.value,
placeholder: Image(
image: provider,
fit: BoxFit.contain,
height: context.height,
width: context.width,
alignment: Alignment.center,
),
),
); );
} }
}, },

View File

@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/providers/asset_viewer/native_video_player_controller_provider.dart';
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_controller_provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
import 'package:immich_mobile/widgets/asset_viewer/video_player.dart';
import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart';
import 'package:native_video_player/native_video_player.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
class NativeVideoViewerPage extends ConsumerStatefulWidget {
final Asset asset;
final Widget? placeholder;
const NativeVideoViewerPage({
super.key,
required this.asset,
this.placeholder,
});
@override
NativeVideoViewerPageState createState() => NativeVideoViewerPageState();
}
class NativeVideoViewerPageState extends ConsumerState<NativeVideoViewerPage> {
@override
Widget build(BuildContext context) {
final size = MediaQuery.sizeOf(context);
double videoWidth = size.width;
double videoHeight = size.height;
NativeVideoPlayerController? controller;
void initController(NativeVideoPlayerController videoCtrl) {
controller = videoCtrl;
controller?.onPlaybackReady.addListener(() {
// Emitted when the video loaded successfully and it's ready to play.
// At this point, videoInfo is available.
final videoInfo = controller?.videoInfo;
setState(() {
if (videoInfo != null) {
videoWidth = videoInfo.width.toDouble();
videoHeight = videoInfo.height.toDouble();
print(videoHeight);
print(videoWidth);
}
});
final videoDuration = videoInfo?.duration;
controller?.play();
});
controller?.onPlaybackStatusChanged.addListener(() {
final playbackStatus = controller?.playbackInfo?.status;
// playbackStatus can be playing, paused, or stopped.
});
controller?.onPlaybackPositionChanged.addListener(() {
final playbackPosition = controller?.playbackInfo?.position;
});
controller?.onPlaybackEnded.addListener(() {
// Emitted when the video has finished playing.
});
}
dispose() {
controller = null;
super.dispose();
}
return PopScope(
onPopInvoked: (pop) {
ref.read(videoPlaybackValueProvider.notifier).value =
VideoPlaybackValue.uninitialized();
},
child: SizedBox(
height: videoHeight,
width: videoWidth,
child: AspectRatio(
aspectRatio: 16 / 9,
child: NativeVideoPlayerView(
onViewReady: (c) async {
// Use a local file for the video player controller
final file = await widget.asset.local!.file;
if (file == null) {
throw Exception('No file found for the video');
}
final videoSource = await VideoSource.init(
path: file.path,
type: VideoSourceType.file,
);
await c.loadVideoSource(videoSource);
initController(c);
},
),
),
),
);
}
// final Asset asset;
// final Widget? placeholder;
// final Duration hideControlsTimer;
// final bool showControls;
// final bool showDownloadingIndicator;
// final bool loopVideo;
}

View File

@ -103,7 +103,7 @@ class VideoViewerPage extends HookConsumerWidget {
// Done in a microtask to avoid setting the state while the is building // Done in a microtask to avoid setting the state while the is building
if (!isMotionVideo) { if (!isMotionVideo) {
Future.microtask(() { Future.microtask(() {
ref.read(showControlsProvider.notifier).show = true; ref.read(showControlsProvider.notifier).show = false;
}); });
} }
@ -148,20 +148,16 @@ class VideoViewerPage extends HookConsumerWidget {
), ),
if (controller != null) if (controller != null)
SizedBox( SizedBox(
height: 16 / 9 * size.width, height: size.height,
width: size.width, width: size.width,
child: AspectRatio( child: VideoPlayerViewer(
aspectRatio: 16 / 9, controller: controller,
child: VideoPlayerViewer( isMotionVideo: isMotionVideo,
controller: controller, placeholder: placeholder,
isMotionVideo: isMotionVideo, hideControlsTimer: hideControlsTimer,
placeholder: placeholder, showControls: showControls,
hideControlsTimer: hideControlsTimer, showDownloadingIndicator: showDownloadingIndicator,
showControls: showControls, loopVideo: loopVideo,
showDownloadingIndicator: showDownloadingIndicator,
loopVideo: loopVideo,
asset: asset,
),
), ),
), ),
], ],

View File

@ -0,0 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:native_video_player/native_video_player.dart';
final nativePlayerControllerProvider =
StateProvider((ref) => NativeVideoPlayerController(0));

View File

@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:native_video_player/native_video_player.dart';
import 'package:video_player/video_player.dart';
class NativeVideoPlayer extends HookConsumerWidget {
final VideoPlayerController controller;
final bool isMotionVideo;
final Widget? placeholder;
final Duration hideControlsTimer;
final bool showControls;
final bool showDownloadingIndicator;
final bool loopVideo;
final Asset asset;
const NativeVideoPlayer({
super.key,
required this.controller,
required this.isMotionVideo,
this.placeholder,
required this.hideControlsTimer,
required this.showControls,
required this.showDownloadingIndicator,
required this.loopVideo,
required this.asset,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return NativeVideoPlayerView(
onViewReady: (controller) async {
try {
String path = '';
VideoSourceType type = VideoSourceType.file;
if (asset.isLocal && asset.livePhotoVideoId == null) {
// Use a local file for the video player controller
final file = await asset.local!.file;
if (file == null) {
throw Exception('No file found for the video');
}
path = file.path;
type = VideoSourceType.file;
final videoSource = await VideoSource.init(
path: path,
type: type,
);
await controller.loadVideoSource(videoSource);
await controller.play();
Future.delayed(const Duration(milliseconds: 100), () async {
await controller.setVolume(0.5);
});
}
} catch (e) {
print('Error loading video: $e');
}
},
);
}
}

View File

@ -1,11 +1,8 @@
import 'package:chewie/chewie.dart'; import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/utils/hooks/chewiew_controller_hook.dart'; import 'package:immich_mobile/utils/hooks/chewiew_controller_hook.dart';
import 'package:immich_mobile/widgets/asset_viewer/custom_video_player_controls.dart'; import 'package:immich_mobile/widgets/asset_viewer/custom_video_player_controls.dart';
import 'package:native_video_player/native_video_player.dart';
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
class VideoPlayerViewer extends HookConsumerWidget { class VideoPlayerViewer extends HookConsumerWidget {
@ -16,7 +13,6 @@ class VideoPlayerViewer extends HookConsumerWidget {
final bool showControls; final bool showControls;
final bool showDownloadingIndicator; final bool showDownloadingIndicator;
final bool loopVideo; final bool loopVideo;
final Asset asset;
const VideoPlayerViewer({ const VideoPlayerViewer({
super.key, super.key,
@ -27,59 +23,26 @@ class VideoPlayerViewer extends HookConsumerWidget {
required this.showControls, required this.showControls,
required this.showDownloadingIndicator, required this.showDownloadingIndicator,
required this.loopVideo, required this.loopVideo,
required this.asset,
}); });
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
// final chewie = useChewieController( final chewie = useChewieController(
// controller: controller, controller: controller,
// controlsSafeAreaMinimum: const EdgeInsets.only( controlsSafeAreaMinimum: const EdgeInsets.only(
// bottom: 100, bottom: 100,
// ), ),
// placeholder: SizedBox.expand(child: placeholder), placeholder: SizedBox.expand(child: placeholder),
// customControls: CustomVideoPlayerControls( customControls: CustomVideoPlayerControls(
// hideTimerDuration: hideControlsTimer, hideTimerDuration: hideControlsTimer,
// ), ),
// showControls: showControls && !isMotionVideo, showControls: showControls && !isMotionVideo,
// hideControlsTimer: hideControlsTimer, hideControlsTimer: hideControlsTimer,
// loopVideo: loopVideo, loopVideo: loopVideo,
// ); );
// return Chewie( return Chewie(
// controller: chewie, controller: chewie,
// );
return NativeVideoPlayerView(
onViewReady: (controller) async {
try {
String path = '';
VideoSourceType type = VideoSourceType.file;
if (asset.isLocal && asset.livePhotoVideoId == null) {
// Use a local file for the video player controller
final file = await asset.local!.file;
if (file == null) {
throw Exception('No file found for the video');
}
path = file.path;
type = VideoSourceType.file;
final videoSource = await VideoSource.init(
path: path,
type: type,
);
await controller.loadVideoSource(videoSource);
await controller.play();
Future.delayed(const Duration(milliseconds: 100), () async {
await controller.setVolume(0.5);
});
}
} catch (e) {
print('Error loading video: $e');
}
},
); );
} }
} }