mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-16 16:09:30 +00:00
player: add chapter skip buttons
This commit is contained in:
parent
6557e63a28
commit
36509913f2
4 changed files with 58 additions and 53 deletions
|
|
@ -22,14 +22,21 @@ Duration sumOfTracks(BookExpanded book, int? index) {
|
||||||
|
|
||||||
/// returns the [AudioTrack] to play based on the [position] in the [book]
|
/// returns the [AudioTrack] to play based on the [position] in the [book]
|
||||||
AudioTrack getTrackToPlay(BookExpanded book, Duration position) {
|
AudioTrack getTrackToPlay(BookExpanded book, Duration position) {
|
||||||
var totalDuration = Duration.zero;
|
// var totalDuration = Duration.zero;
|
||||||
for (var track in book.tracks) {
|
// for (var track in book.tracks) {
|
||||||
totalDuration += track.duration;
|
// totalDuration += track.duration;
|
||||||
if (totalDuration >= position) {
|
// if (totalDuration >= position) {
|
||||||
return track;
|
// return track;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return book.tracks.last;
|
// return book.tracks.last;
|
||||||
|
return book.tracks.firstWhere(
|
||||||
|
(element) {
|
||||||
|
return element.startOffset <= position &&
|
||||||
|
(element.startOffset + element.duration) >= position;
|
||||||
|
},
|
||||||
|
orElse: () => book.tracks.last,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// will manage the audio player instance
|
/// will manage the audio player instance
|
||||||
|
|
@ -90,11 +97,11 @@ class AudiobookPlayer extends AudioPlayer {
|
||||||
// hence first we need to calculate the current track which will be used to set the initial position
|
// hence first we need to calculate the current track which will be used to set the initial position
|
||||||
// then we set the initial index to the current track index and position as the remaining duration from the position
|
// then we set the initial index to the current track index and position as the remaining duration from the position
|
||||||
// after subtracting the duration of all the previous tracks
|
// after subtracting the duration of all the previous tracks
|
||||||
|
// initialPosition ;
|
||||||
final trackToPlay = getTrackToPlay(book, initialPosition ?? Duration.zero);
|
final trackToPlay = getTrackToPlay(book, initialPosition ?? Duration.zero);
|
||||||
final initialIndex = book.tracks.indexOf(trackToPlay);
|
final initialIndex = book.tracks.indexOf(trackToPlay);
|
||||||
final initialPositionInTrack = initialPosition != null
|
final initialPositionInTrack = initialPosition != null
|
||||||
? initialPosition - sumOfTracks(book, initialIndex - 1)
|
? initialPosition - trackToPlay.startOffset
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
await setAudioSource(
|
await setAudioSource(
|
||||||
|
|
@ -131,7 +138,7 @@ class AudiobookPlayer extends AudioPlayer {
|
||||||
throw StateError('No book is set');
|
throw StateError('No book is set');
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO refactor this
|
// TODO refactor this to cover all the states
|
||||||
return switch (playerState) {
|
return switch (playerState) {
|
||||||
PlayerState(playing: var isPlaying) => isPlaying ? pause() : play(),
|
PlayerState(playing: var isPlaying) => isPlaying ? pause() : play(),
|
||||||
};
|
};
|
||||||
|
|
@ -142,30 +149,48 @@ class AudiobookPlayer extends AudioPlayer {
|
||||||
/// so we need to calculate the duration and current position based on the book
|
/// so we need to calculate the duration and current position based on the book
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> seek(Duration? position, {int? index}) async {
|
Future<void> seek(Duration? positionInBook, {int? index}) async {
|
||||||
if (_book == null) {
|
if (_book == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (position == null) {
|
if (positionInBook == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final trackToPlay = getTrackToPlay(_book!, position);
|
final tracks = _book!.tracks;
|
||||||
final index = _book!.tracks.indexOf(trackToPlay);
|
final trackToPlay = getTrackToPlay(_book!, positionInBook);
|
||||||
final positionInTrack = position - sumOfTracks(_book!, index - 1);
|
final index = tracks.indexOf(trackToPlay);
|
||||||
|
final positionInTrack = positionInBook - trackToPlay.startOffset;
|
||||||
return super.seek(positionInTrack, index: index);
|
return super.seek(positionInTrack, index: index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a convenience method to get position in the book instead of the current track position
|
||||||
|
Duration get positionInBook {
|
||||||
|
if (_book == null) {
|
||||||
|
return Duration.zero;
|
||||||
|
}
|
||||||
|
return position + _book!.tracks[sequenceState!.currentIndex].startOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a convenience method to get the buffered position in the book instead of the current track position
|
||||||
|
Duration get bufferedPositionInBook {
|
||||||
|
if (_book == null) {
|
||||||
|
return Duration.zero;
|
||||||
|
}
|
||||||
|
return bufferedPosition + _book!.tracks[sequenceState!.currentIndex].startOffset;
|
||||||
|
}
|
||||||
|
|
||||||
/// streams to override to suit the book instead of the current track
|
/// streams to override to suit the book instead of the current track
|
||||||
// - positionStream
|
// - positionStream
|
||||||
// - bufferedPositionStream
|
// - bufferedPositionStream
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Duration> get positionStream {
|
Stream<Duration> get positionStream {
|
||||||
|
// return the positioninbook stream
|
||||||
return super.positionStream.map((position) {
|
return super.positionStream.map((position) {
|
||||||
if (_book == null) {
|
if (_book == null) {
|
||||||
return Duration.zero;
|
return Duration.zero;
|
||||||
}
|
}
|
||||||
return position + sumOfTracks(_book!, sequenceState!.currentIndex);
|
return position + _book!.tracks[sequenceState!.currentIndex].startOffset;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,7 +200,7 @@ class AudiobookPlayer extends AudioPlayer {
|
||||||
if (_book == null) {
|
if (_book == null) {
|
||||||
return Duration.zero;
|
return Duration.zero;
|
||||||
}
|
}
|
||||||
return position + sumOfTracks(_book!, sequenceState!.currentIndex);
|
return position + _book!.tracks[sequenceState!.currentIndex].startOffset;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,12 +211,9 @@ class AudiobookPlayer extends AudioPlayer {
|
||||||
}
|
}
|
||||||
return _book!.chapters.firstWhere(
|
return _book!.chapters.firstWhere(
|
||||||
(element) {
|
(element) {
|
||||||
return element.start <= position && element.end >= position;
|
return element.start <= positionInBook && element.end >= positionInBook;
|
||||||
},
|
},
|
||||||
orElse: () => _book!.chapters.first,
|
orElse: () => _book!.chapters.first,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,7 @@ BookExpanded? currentlyPlayingBook(CurrentlyPlayingBookRef ref) {
|
||||||
@riverpod
|
@riverpod
|
||||||
BookChapter? currentPlayingChapter(CurrentPlayingChapterRef ref) {
|
BookChapter? currentPlayingChapter(CurrentPlayingChapterRef ref) {
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
// get the current timestamp
|
return player.currentChapter;
|
||||||
final currentTimestamp = player.position;
|
|
||||||
// get the chapter that contains the current timestamp
|
|
||||||
return player.book?.chapters.firstWhere(
|
|
||||||
(element) =>
|
|
||||||
element.start <= currentTimestamp && element.end >= currentTimestamp,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// provides the book metadata of the currently playing book
|
/// provides the book metadata of the currently playing book
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ final currentlyPlayingBookProvider =
|
||||||
|
|
||||||
typedef CurrentlyPlayingBookRef = AutoDisposeProviderRef<BookExpanded?>;
|
typedef CurrentlyPlayingBookRef = AutoDisposeProviderRef<BookExpanded?>;
|
||||||
String _$currentPlayingChapterHash() =>
|
String _$currentPlayingChapterHash() =>
|
||||||
r'562416b7e0068aaba9138cb8e0ed7a5ddba8e6c6';
|
r'3a621260211cddecfd974b31d5c4820ed24b1545';
|
||||||
|
|
||||||
/// provided the current chapter of the book being played
|
/// provided the current chapter of the book being played
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
@ -270,6 +269,7 @@ class AudiobookChapterProgressBar extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
|
final currentChapter = ref.watch(currentPlayingChapterProvider);
|
||||||
final position = useStream(
|
final position = useStream(
|
||||||
player.positionStream,
|
player.positionStream,
|
||||||
initialData: const Duration(seconds: 0),
|
initialData: const Duration(seconds: 0),
|
||||||
|
|
@ -278,34 +278,22 @@ class AudiobookChapterProgressBar extends HookConsumerWidget {
|
||||||
player.bufferedPositionStream,
|
player.bufferedPositionStream,
|
||||||
initialData: const Duration(seconds: 0),
|
initialData: const Duration(seconds: 0),
|
||||||
);
|
);
|
||||||
final currentIndex = useStream(
|
|
||||||
player.currentIndexStream,
|
|
||||||
initialData: 0,
|
|
||||||
);
|
|
||||||
final durationOfPreviousTracks =
|
|
||||||
player.book?.tracks.sublist(0, currentIndex.data).fold(
|
|
||||||
const Duration(seconds: 0),
|
|
||||||
(previousValue, element) => previousValue + element.duration,
|
|
||||||
) ??
|
|
||||||
const Duration(seconds: 0);
|
|
||||||
final totalProgress = durationOfPreviousTracks +
|
|
||||||
(position.data ?? const Duration(seconds: 0));
|
|
||||||
final totalBuffered = durationOfPreviousTracks +
|
|
||||||
(buffered.data ?? const Duration(seconds: 0));
|
|
||||||
// now find the chapter that corresponds to the current time
|
// now find the chapter that corresponds to the current time
|
||||||
// and calculate the progress of the current chapter
|
// and calculate the progress of the current chapter
|
||||||
final currentChapter = player.book?.chapters.firstWhereOrNull(
|
|
||||||
(element) =>
|
|
||||||
(element.start <= totalProgress) && (element.end >= totalProgress),
|
|
||||||
);
|
|
||||||
final currentChapterProgress =
|
final currentChapterProgress =
|
||||||
currentChapter == null ? null : (totalProgress - currentChapter.start);
|
currentChapter == null
|
||||||
|
? null
|
||||||
|
: (player.positionInBook - currentChapter.start);
|
||||||
|
|
||||||
final currentChapterBuffered =
|
final currentChapterBuffered =
|
||||||
currentChapter == null ? null : (totalBuffered - currentChapter.start);
|
currentChapter == null
|
||||||
|
? null
|
||||||
|
: (player.bufferedPositionInBook - currentChapter.start);
|
||||||
|
|
||||||
return ProgressBar(
|
return ProgressBar(
|
||||||
progress: currentChapterProgress ?? totalProgress,
|
progress:
|
||||||
|
currentChapterProgress ?? position.data ?? const Duration(seconds: 0),
|
||||||
total: currentChapter == null
|
total: currentChapter == null
|
||||||
? player.book?.duration ?? const Duration(seconds: 0)
|
? player.book?.duration ?? const Duration(seconds: 0)
|
||||||
: currentChapter.end - currentChapter.start,
|
: currentChapter.end - currentChapter.start,
|
||||||
|
|
@ -316,7 +304,8 @@ class AudiobookChapterProgressBar extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
thumbRadius: 8,
|
thumbRadius: 8,
|
||||||
buffered: currentChapterBuffered ?? totalBuffered,
|
buffered:
|
||||||
|
currentChapterBuffered ?? buffered.data ?? const Duration(seconds: 0),
|
||||||
bufferedBarColor: Theme.of(context).colorScheme.secondary,
|
bufferedBarColor: Theme.of(context).colorScheme.secondary,
|
||||||
timeLabelType: TimeLabelType.remainingTime,
|
timeLabelType: TimeLabelType.remainingTime,
|
||||||
timeLabelLocation: TimeLabelLocation.below,
|
timeLabelLocation: TimeLabelLocation.below,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue