104 lines
2.8 KiB
Dart
104 lines
2.8 KiB
Dart
import "dart:async";
|
|
|
|
import "package:flutter/material.dart";
|
|
import "package:flutter_hooks/flutter_hooks.dart";
|
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
|
import "package:media_kit/media_kit.dart";
|
|
import "package:nexus/helpers/extensions/get_headers.dart";
|
|
import "package:nexus/models/info/audio.dart";
|
|
|
|
class AudioPlayer extends HookConsumerWidget {
|
|
final Uri url;
|
|
final AudioInfo? info;
|
|
|
|
const AudioPlayer(this.url, this.info, {super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final player = useMemoized(
|
|
() => Player(
|
|
configuration: PlayerConfiguration(bufferSize: 128 * 1024 * 1024),
|
|
),
|
|
);
|
|
|
|
final playing = useState(false);
|
|
final position = useState(Duration.zero);
|
|
final duration = useState(Duration.zero);
|
|
|
|
useEffect(() {
|
|
scheduleMicrotask(() async {
|
|
await player.open(
|
|
Media(url.toString(), httpHeaders: ref.headers),
|
|
play: false,
|
|
);
|
|
|
|
player.stream.playing.listen((value) {
|
|
playing.value = value;
|
|
});
|
|
|
|
player.stream.position.listen((value) {
|
|
position.value = value;
|
|
});
|
|
|
|
player.stream.duration.listen((value) {
|
|
duration.value = value;
|
|
});
|
|
});
|
|
|
|
return player.dispose;
|
|
}, []);
|
|
|
|
String format(Duration duration) {
|
|
final minutes = duration.inMinutes
|
|
.remainder(60)
|
|
.toString()
|
|
.padLeft(2, "0");
|
|
final seconds = duration.inSeconds
|
|
.remainder(60)
|
|
.toString()
|
|
.padLeft(2, "0");
|
|
|
|
return "$minutes:$seconds";
|
|
}
|
|
|
|
return SizedBox(
|
|
height: 60,
|
|
child: Card(
|
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
|
child: Padding(
|
|
padding: EdgeInsetsGeometry.only(left: 8, right: 16),
|
|
child: Row(
|
|
children: [
|
|
IconButton(
|
|
onPressed: player.playOrPause,
|
|
icon: Icon(
|
|
playing.value ? Icons.pause_circle : Icons.play_circle,
|
|
),
|
|
),
|
|
SizedBox(width: 8),
|
|
Text(
|
|
format(position.value),
|
|
style: Theme.of(context).textTheme.bodySmall,
|
|
),
|
|
Expanded(
|
|
child: Slider(
|
|
min: 0,
|
|
max: duration.value.inMilliseconds <= 0
|
|
? 1
|
|
: duration.value.inMilliseconds.toDouble(),
|
|
value: position.value.inMilliseconds.toDouble(),
|
|
onChanged: (value) =>
|
|
player.seek(Duration(milliseconds: value.toInt())),
|
|
),
|
|
),
|
|
Text(
|
|
format(duration.value),
|
|
style: Theme.of(context).textTheme.bodySmall,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|