mirror of
https://github.com/immich-app/immich.git
synced 2025-06-23 15:30:51 -04:00
* initial cast framework complete and mocked cast dialog working * wip casting * casting works! just need to add session key check and remote video controls * cleanup of classes * add session expiration checks * cast dialog now shows connected device at top of list with a list header. Discovered devices are also cached for app session. * cast video player finalized * show fullsize assets on casting * translation already happens on the text element * remove prints * fix lintings * code review changes from @shenlong-tanwen * fix connect method override * fix alphabetization * remove important * filter chromecast audio devices * fix some disconnect command ordering issues and unawaited futures * remove prints * only disconnect if we are connected * don't try to reconnect if its the current device * add cast button to top bar * format sessions api * more formatting issues fixed * add snack bar to tell user that we cannot cast an asset that is not uploaded to server * make casting icon change to primary color when casting is active * only show casting snackbar if we are casting * dont show cast button if asset is remote and we are not casting * stop playing media if we seek to an asset that is not remote * remove https check since it works with local http IP addresses * remove unneeded imports * fix recasting when socket closes * fix info plist formatting * only show cast button if there is an active websocket connection (ie the server is accessible) * add device capability bitmask checks * small comment about bitmask
132 lines
4.4 KiB
Dart
132 lines
4.4 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:immich_mobile/constants/colors.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/providers/cast.provider.dart';
|
|
import 'package:immich_mobile/widgets/asset_viewer/formatted_duration.dart';
|
|
|
|
class VideoPosition extends HookConsumerWidget {
|
|
const VideoPosition({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final isCasting = ref.watch(castProvider).isCasting;
|
|
|
|
final (position, duration) = isCasting
|
|
? ref.watch(
|
|
castProvider.select((c) => (c.currentTime, c.duration)),
|
|
)
|
|
: ref.watch(
|
|
videoPlaybackValueProvider.select((v) => (v.position, v.duration)),
|
|
);
|
|
|
|
final wasPlaying = useRef<bool>(true);
|
|
return duration == Duration.zero
|
|
? const _VideoPositionPlaceholder()
|
|
: Column(
|
|
children: [
|
|
Padding(
|
|
// align with slider's inherent padding
|
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
FormattedDuration(position),
|
|
FormattedDuration(duration),
|
|
],
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Slider(
|
|
value: min(
|
|
position.inMicroseconds / duration.inMicroseconds * 100,
|
|
100,
|
|
),
|
|
min: 0,
|
|
max: 100,
|
|
thumbColor: Colors.white,
|
|
activeColor: Colors.white,
|
|
inactiveColor: whiteOpacity75,
|
|
onChangeStart: (value) {
|
|
final state =
|
|
ref.read(videoPlaybackValueProvider).state;
|
|
wasPlaying.value = state != VideoPlaybackState.paused;
|
|
ref.read(videoPlayerControlsProvider.notifier).pause();
|
|
},
|
|
onChangeEnd: (value) {
|
|
if (wasPlaying.value) {
|
|
ref.read(videoPlayerControlsProvider.notifier).play();
|
|
}
|
|
},
|
|
onChanged: (value) {
|
|
final seekToDuration = (duration * (value / 100.0));
|
|
|
|
if (isCasting) {
|
|
ref
|
|
.read(castProvider.notifier)
|
|
.seekTo(seekToDuration);
|
|
return;
|
|
}
|
|
|
|
ref
|
|
.read(videoPlayerControlsProvider.notifier)
|
|
.position = seekToDuration.inSeconds.toDouble();
|
|
|
|
// This immediately updates the slider position without waiting for the video to update
|
|
ref.read(videoPlaybackValueProvider.notifier).position =
|
|
seekToDuration;
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class _VideoPositionPlaceholder extends StatelessWidget {
|
|
const _VideoPositionPlaceholder();
|
|
|
|
static void _onChangedDummy(_) {}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return const Column(
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
FormattedDuration(Duration.zero),
|
|
FormattedDuration(Duration.zero),
|
|
],
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Slider(
|
|
value: 0.0,
|
|
min: 0,
|
|
max: 100,
|
|
thumbColor: Colors.white,
|
|
activeColor: Colors.white,
|
|
inactiveColor: whiteOpacity75,
|
|
onChanged: _onChangedDummy,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|