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

View file

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

View file

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