migrate to just audio

This commit is contained in:
Dr-Blank 2024-05-17 11:04:20 -04:00
parent a1dd0e9d3f
commit 01b3dead49
No known key found for this signature in database
GPG key ID: 7452CC63F210A266
22 changed files with 1062 additions and 340 deletions

View file

@ -1,13 +1,14 @@
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:just_audio/just_audio.dart';
import 'package:miniplayer/miniplayer.dart';
import 'package:whispering_pages/api/image_provider.dart';
import 'package:whispering_pages/api/library_item_provider.dart';
import 'package:whispering_pages/features/player/providers/audiobook_player.dart';
import 'package:whispering_pages/features/player/providers/currently_playing_provider.dart';
import 'package:whispering_pages/features/player/providers/player_form.dart';
import 'package:whispering_pages/shared/widgets/shelves/book_shelf.dart';
import 'package:whispering_pages/theme/theme_from_cover_provider.dart';
@ -26,13 +27,7 @@ double percentageFromValueInRange({required final double min, max, value}) {
return (value - min) / (max - min);
}
const double playerMinHeight = 70;
const double playerMaxHeight = 500;
const miniplayerPercentageDeclaration = 0.2;
final ValueNotifier<double> playerExpandProgress =
ValueNotifier(playerMinHeight);
final MiniplayerController controller = MiniplayerController();
class AudiobookPlayer extends HookConsumerWidget {
const AudiobookPlayer({super.key});
@ -64,39 +59,19 @@ class AudiobookPlayer extends HookConsumerWidget {
);
// add controller to the player state listener
player.onPlayerStateChanged.listen((state) {
if (state == PlayerState.playing) {
playPauseController.reverse();
} else {
player.playerStateStream.listen((state) {
if (state.playing) {
playPauseController.forward();
} else {
playPauseController.reverse();
}
});
final playPauseButton = IconButton(
onPressed: () async {
await player.togglePlayPause();
},
icon: AnimatedIcon(
icon: AnimatedIcons.pause_play,
progress: playPauseController,
size: 50,
),
final playPauseButton = AudiobookPlayerPlayPauseButton(
playPauseController: playPauseController,
);
// player.onPositionChanged.listen((event) {
// currentProgress.value = event.inSeconds.toDouble();
// });
// final progressStream = TimerPositionUpdater(
// getPosition: player.getCurrentPosition,
// interval: const Duration(milliseconds: 500),
// ).positionStream;
// // a debug that will print the current position of the player
// progressStream.listen((event) {
// debugPrint('Current position: ${event.inSeconds}');
// });
// the widget that will be displayed when the player is expanded
const progressBar = PlayerProgressBar();
const progressBar = AudiobookTotalProgressBar();
// theme from image
final imageTheme = ref.watch(
@ -105,30 +80,38 @@ class AudiobookPlayer extends HookConsumerWidget {
brightness: Theme.of(context).brightness,
),
);
return Theme(
// get the theme from imageThemeProvider
// max height of the player is the height of the screen
final playerMaxHeight = MediaQuery.of(context).size.height;
return Theme(
data: ThemeData(
colorScheme: imageTheme.valueOrNull ?? Theme.of(context).colorScheme,
),
child: Miniplayer(
valueNotifier: playerExpandProgress,
valueNotifier: ref.watch(playerExpandProgressNotifierProvider),
minHeight: playerMinHeight,
maxHeight: playerMaxHeight,
controller: controller,
controller: ref.watch(miniplayerControllerProvider),
elevation: 4,
onDismissed: () {
player.setSourceAudioBook(null);
},
curve: Curves.easeOut,
builder: (height, percentage) {
// return SafeArea(
// child: Text(
// 'percentage: ${percentage.toStringAsFixed(2)}, height: ${height.toStringAsFixed(2)}',
// ),
// );
final bool isFormMiniplayer =
percentage < miniplayerPercentageDeclaration;
final double availWidth = MediaQuery.of(context).size.width;
final maxImgSize = availWidth * 0.4;
final bookTitle = Text(player.book?.metadata.title ?? '');
//Declare additional widgets (eg. SkipButton) and variables
if (!isFormMiniplayer) {
var percentageExpandedPlayer = percentageFromValueInRange(
@ -153,7 +136,7 @@ class AudiobookPlayer extends HookConsumerWidget {
percentage: percentageExpandedPlayer,
) /
2;
const buttonSkipForward = IconButton(
icon: Icon(Icons.forward_30),
iconSize: 33,
@ -164,12 +147,6 @@ class AudiobookPlayer extends HookConsumerWidget {
iconSize: 33,
onPressed: onTap,
);
const buttonPlayExpanded = IconButton(
icon: Icon(Icons.pause_circle_filled),
iconSize: 50,
onPressed: onTap,
);
return PlayerWhenExpanded(
imgPaddingLeft: paddingLeft,
imgPaddingVertical: paddingVertical,
@ -178,12 +155,12 @@ class AudiobookPlayer extends HookConsumerWidget {
percentageExpandedPlayer: percentageExpandedPlayer,
text: bookTitle,
buttonSkipBackwards: buttonSkipBackwards,
buttonPlayExpanded: playPauseButton,
playPauseButton: playPauseButton,
buttonSkipForward: buttonSkipForward,
progressIndicator: progressBar,
);
}
//Miniplayer
final percentageMiniplayer = percentageFromValueInRange(
min: playerMinHeight,
@ -191,16 +168,14 @@ class AudiobookPlayer extends HookConsumerWidget {
playerMinHeight,
value: height,
);
final elementOpacity = 1 - 1 * percentageMiniplayer;
final progressIndicatorHeight = 4 - 4 * percentageMiniplayer;
return PlayerWhenMinimized(
maxImgSize: maxImgSize,
imgWidget: imgWidget,
elementOpacity: elementOpacity,
playPauseButton: playPauseButton,
progressIndicatorHeight: progressIndicatorHeight,
progressIndicator: progressBar,
);
},
@ -209,34 +184,102 @@ class AudiobookPlayer extends HookConsumerWidget {
}
}
class PlayerProgressBar extends HookConsumerWidget {
const PlayerProgressBar({
class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
const AudiobookPlayerPlayPauseButton({
super.key,
required this.playPauseController,
});
final AnimationController playPauseController;
@override
Widget build(BuildContext context, WidgetRef ref) {
final player = ref.watch(audiobookPlayerProvider);
return switch (player.processingState) {
ProcessingState.loading ||
ProcessingState.buffering =>
const CircularProgressIndicator(),
ProcessingState.completed => IconButton(
onPressed: () async {
await player.seek(const Duration(seconds: 0));
await player.play();
},
icon: const Icon(Icons.replay),
),
ProcessingState.ready => IconButton(
onPressed: () async {
await player.togglePlayPause();
},
icon: AnimatedIcon(
icon: AnimatedIcons.play_pause,
progress: playPauseController,
size: 50,
),
),
ProcessingState.idle => const SizedBox.shrink(),
};
}
}
/// A progress bar that shows the total progress of the audiobook
///
/// for chapter progress, use [AudiobookChapterProgressBar]
class AudiobookTotalProgressBar extends HookConsumerWidget {
const AudiobookTotalProgressBar({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final player = ref.watch(audiobookPlayerProvider);
final playerState = useState(player.state);
// final playerState = useState(player.processingState);
// add a listener to the player state
player.onPlayerStateChanged.listen((state) {
playerState.value = state;
});
return StreamBuilder<Duration>(
stream: player.onPositionChanged,
builder: (context, snapshot) {
return ProgressBar(
progress: snapshot.data ?? const Duration(seconds: 0),
total: player.book?.duration ?? const Duration(seconds: 0),
onSeek: player.seek,
thumbRadius: 8,
// thumbColor: Theme.of(context).colorScheme.secondary,
thumbGlowColor: Theme.of(context).colorScheme.secondary,
thumbGlowRadius: playerState.value == PlayerState.playing ? 10 : 0,
// player.processingStateStream.listen((state) {
// playerState.value = state;
// });
return StreamBuilder(
stream: player.currentIndexStream,
builder: (context, currentTrackIndex) {
return StreamBuilder(
stream: player.positionStream,
builder: (context, progress) {
// totalProgress is the sum of the duration of all the tracks before the current track + the current track position
final totalProgress =
player.book?.tracks.sublist(0, currentTrackIndex.data).fold(
const Duration(seconds: 0),
(previousValue, element) =>
previousValue + element.duration,
) ??
const Duration(seconds: 0) +
(progress.data ?? const Duration(seconds: 0));
return StreamBuilder(
stream: player.bufferedPositionStream,
builder: (context, buffered) {
final totalBuffered =
player.book?.tracks.sublist(0, currentTrackIndex.data).fold(
const Duration(seconds: 0),
(previousValue, element) =>
previousValue + element.duration,
) ??
const Duration(seconds: 0) +
(buffered.data ?? const Duration(seconds: 0));
return ProgressBar(
progress: totalProgress,
total: player.book?.duration ?? const Duration(seconds: 0),
onSeek: player.seek,
thumbRadius: 8,
buffered: totalBuffered,
bufferedBarColor: Theme.of(context).colorScheme.secondary,
);
},
);
},
);
},
);
}
}
// ! TODO remove onTap
void onTap() {}