0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-03-11 02:23:09 -05:00

separate video loader widget

This commit is contained in:
Mert Alev 2024-11-02 21:58:39 -04:00
parent 338e5a8e5c
commit 7ed2c68c46
3 changed files with 79 additions and 47 deletions

View file

@ -33,25 +33,28 @@ class NativeVideoLoader extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final log = Logger('NativeVideoLoader');
log.info('Building NativeVideoLoader');
// fast path for aspect ratio // fast path for aspect ratio
final initAspectRatio = useMemoized( // final initAspectRatio = useMemoized(
() { // () {
if (asset.exifInfo == null) { // if (asset.exifInfo == null) {
return null; // return null;
} // }
final width = asset.orientatedWidth?.toDouble(); // final width = asset.orientatedWidth?.toDouble();
final height = asset.orientatedHeight?.toDouble(); // final height = asset.orientatedHeight?.toDouble();
return width != null && height != null && width > 0 && height > 0 // return width != null && height != null && width > 0 && height > 0
? width / height // ? width / height
: null; // : null;
}, // },
); // );
final localEntity = useMemoized( final localEntity = useMemoized(
() => asset.localId != null ? AssetEntity.fromId(asset.localId!) : null, () => asset.isLocal ? AssetEntity.fromId(asset.localId!) : null,
); );
Future<double> calculateAspectRatio() async { Future<double> calculateAspectRatio() async {
log.info('Calculating aspect ratio');
late final double? orientatedWidth; late final double? orientatedWidth;
late final double? orientatedHeight; late final double? orientatedHeight;
@ -68,6 +71,7 @@ class NativeVideoLoader extends HookConsumerWidget {
orientatedHeight = entity.orientatedHeight?.toDouble(); orientatedHeight = entity.orientatedHeight?.toDouble();
} }
log.info('Calculated aspect ratio');
if (orientatedWidth != null && if (orientatedWidth != null &&
orientatedHeight != null && orientatedHeight != null &&
orientatedWidth > 0 && orientatedWidth > 0 &&
@ -78,12 +82,7 @@ class NativeVideoLoader extends HookConsumerWidget {
return 1.0; return 1.0;
} }
final aspectRatioFuture = useMemoized( final aspectRatioFuture = useMemoized(() => calculateAspectRatio());
() => initAspectRatio == null ? calculateAspectRatio() : null,
);
final log = Logger('NativeVideoLoader');
log.info('Building NativeVideoLoader');
Future<VideoSource> createLocalSource() async { Future<VideoSource> createLocalSource() async {
log.info('Loading video from local storage'); log.info('Loading video from local storage');
@ -97,10 +96,12 @@ class NativeVideoLoader extends HookConsumerWidget {
throw Exception('No file found for the video'); throw Exception('No file found for the video');
} }
return await VideoSource.init( final source = await VideoSource.init(
path: file.path, path: file.path,
type: VideoSourceType.file, type: VideoSourceType.file,
); );
log.info('Loaded video from local storage');
return source;
} }
Future<VideoSource> createRemoteSource() async { Future<VideoSource> createRemoteSource() async {
@ -112,14 +113,16 @@ class NativeVideoLoader extends HookConsumerWidget {
? '$serverEndpoint/assets/${asset.livePhotoVideoId}/video/playback' ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/video/playback'
: '$serverEndpoint/assets/${asset.remoteId}/video/playback'; : '$serverEndpoint/assets/${asset.remoteId}/video/playback';
return await VideoSource.init( final source = await VideoSource.init(
path: videoUrl, path: videoUrl,
type: VideoSourceType.network, type: VideoSourceType.network,
headers: ApiService.getRequestHeaders(), headers: ApiService.getRequestHeaders(),
); );
log.info('Loaded video from server');
return source;
} }
Future<VideoSource> createSource(Asset asset) async { Future<VideoSource> createSource() {
if (asset.isLocal && asset.livePhotoVideoId == null) { if (asset.isLocal && asset.livePhotoVideoId == null) {
return createLocalSource(); return createLocalSource();
} }
@ -127,6 +130,16 @@ class NativeVideoLoader extends HookConsumerWidget {
return createRemoteSource(); return createRemoteSource();
} }
final createSourceFuture = useMemoized(() => createSource());
final combinedFuture = useMemoized(
() async {
final aspectRatio = await aspectRatioFuture;
final source = await createSourceFuture;
return (source, aspectRatio);
},
);
final size = MediaQuery.sizeOf(context); final size = MediaQuery.sizeOf(context);
return SizedBox( return SizedBox(
@ -143,18 +156,17 @@ class NativeVideoLoader extends HookConsumerWidget {
width: size.width, width: size.width,
child: FutureBuilder( child: FutureBuilder(
key: ValueKey(asset.id), key: ValueKey(asset.id),
future: aspectRatioFuture, future: combinedFuture,
initialData: initAspectRatio, // initialData: initAspectRatio,
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return placeholder; return placeholder;
} }
return NativeVideoViewerPage( return NativeVideoViewerPage(
key: ValueKey(asset.id), videoSource: snapshot.data!.$1,
videoSource: createSource(asset),
duration: asset.duration, duration: asset.duration,
aspectRatio: snapshot.data as double, aspectRatio: snapshot.data!.$2,
isMotionVideo: isMotionVideo, isMotionVideo: isMotionVideo,
hideControlsTimer: hideControlsTimer, hideControlsTimer: hideControlsTimer,
loopVideo: loopVideo, loopVideo: loopVideo,

View file

@ -30,7 +30,7 @@ import 'package:wakelock_plus/wakelock_plus.dart';
// } // }
class NativeVideoViewerPage extends HookConsumerWidget { class NativeVideoViewerPage extends HookConsumerWidget {
final Future<VideoSource> videoSource; final VideoSource videoSource;
final double aspectRatio; final double aspectRatio;
final Duration duration; final Duration duration;
final bool isMotionVideo; final bool isMotionVideo;
@ -75,7 +75,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
} }
// timer to mark videos as buffering if the position does not change // timer to mark videos as buffering if the position does not change
useInterval(const Duration(seconds: 5), checkIfBuffering); // useInterval(const Duration(seconds: 5), checkIfBuffering);
// When the volume changes, set the volume // When the volume changes, set the volume
ref.listen(videoPlayerControlsProvider.select((value) => value.mute), ref.listen(videoPlayerControlsProvider.select((value) => value.mute),
@ -186,12 +186,12 @@ class NativeVideoViewerPage extends HookConsumerWidget {
if (state == VideoPlaybackState.playing) { if (state == VideoPlaybackState.playing) {
log.info('Syncing with the controls playing'); log.info('Syncing with the controls playing');
// Sync with the controls playing // Sync with the controls playing
WakelockPlus.enable(); // WakelockPlus.enable();
log.info('Synced with the controls playing'); log.info('Synced with the controls playing');
} else { } else {
log.info('Syncing with the controls pause'); log.info('Syncing with the controls pause');
// Sync with the controls pause // Sync with the controls pause
WakelockPlus.disable(); // WakelockPlus.disable();
log.info('Synced with the controls pause'); log.info('Synced with the controls pause');
} }
} }
@ -233,7 +233,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
log.info('initController: added onPlaybackEnded listener'); log.info('initController: added onPlaybackEnded listener');
log.info('initController: loading video source'); log.info('initController: loading video source');
nc.loadVideoSource(await videoSource); nc.loadVideoSource(videoSource);
log.info('initController: loaded video source'); log.info('initController: loaded video source');
log.info('initController: setting controller'); log.info('initController: setting controller');
@ -280,26 +280,24 @@ class NativeVideoViewerPage extends HookConsumerWidget {
playerController.onPlaybackEnded.removeListener(onPlaybackEnded); playerController.onPlaybackEnded.removeListener(onPlaybackEnded);
log.info('Removed onPlaybackEnded listener'); log.info('Removed onPlaybackEnded listener');
Future.microtask(() async { log.info('Stopping video');
log.info('Stopping video'); playerController.stop();
await playerController.stop(); log.info('Stopped video');
log.info('Stopped video');
log.info('Disabling wakelock');
await WakelockPlus.disable();
log.info('Disabled wakelock');
});
log.info('Disposing controller'); log.info('Disposing controller');
controller.value = null; controller.value = null;
log.info('Disposed controller'); log.info('Disposed controller');
// log.info('Disabling Wakelock');
// WakelockPlus.disable();
// log.info('Disabled Wakelock');
} catch (error) { } catch (error) {
log.severe('Error during useEffect cleanup: $error'); log.severe('Error during useEffect cleanup: $error');
// Consume error from the controller // Consume error from the controller
} }
}; };
}, },
[], [videoSource],
); );
return Stack( return Stack(

View file

@ -36,6 +36,10 @@ class VideoPlayerControls extends StateNotifier<VideoPlaybackControls> {
} }
void reset() { void reset() {
if (state.position == 0 && !state.mute && !state.pause) {
return;
}
state = VideoPlaybackControls( state = VideoPlaybackControls(
position: 0, position: 0,
pause: false, pause: false,
@ -47,6 +51,10 @@ class VideoPlayerControls extends StateNotifier<VideoPlaybackControls> {
bool get mute => state.mute; bool get mute => state.mute;
set position(double value) { set position(double value) {
if (state.position == value) {
return;
}
state = VideoPlaybackControls( state = VideoPlaybackControls(
position: value, position: value,
mute: state.mute, mute: state.mute,
@ -55,6 +63,10 @@ class VideoPlayerControls extends StateNotifier<VideoPlaybackControls> {
} }
set mute(bool value) { set mute(bool value) {
if (state.mute == value) {
return;
}
state = VideoPlaybackControls( state = VideoPlaybackControls(
position: state.position, position: state.position,
mute: value, mute: value,
@ -71,6 +83,10 @@ class VideoPlayerControls extends StateNotifier<VideoPlaybackControls> {
} }
void pause() { void pause() {
if (state.pause) {
return;
}
state = VideoPlaybackControls( state = VideoPlaybackControls(
position: state.position, position: state.position,
mute: state.mute, mute: state.mute,
@ -79,6 +95,10 @@ class VideoPlayerControls extends StateNotifier<VideoPlaybackControls> {
} }
void play() { void play() {
if (!state.pause) {
return;
}
state = VideoPlaybackControls( state = VideoPlaybackControls(
position: state.position, position: state.position,
mute: state.mute, mute: state.mute,
@ -95,11 +115,13 @@ class VideoPlayerControls extends StateNotifier<VideoPlaybackControls> {
} }
void restart() { void restart() {
state = VideoPlaybackControls( if (state.position > 0 || !state.pause) {
position: 0, state = VideoPlaybackControls(
mute: state.mute, position: 0,
pause: true, mute: state.mute,
); pause: false,
);
}
state = VideoPlaybackControls( state = VideoPlaybackControls(
position: 0, position: 0,