From 36509913f27ec76168e2ea4887d66403bf11fcac Mon Sep 17 00:00:00 2001 From: Dr-Blank <64108942+Dr-Blank@users.noreply.github.com> Date: Sun, 19 May 2024 09:45:41 -0400 Subject: [PATCH] player: add chapter skip buttons --- .../player/core/audiobook_player.dart | 66 ++++++++++++------- .../providers/currently_playing_provider.dart | 8 +-- .../currently_playing_provider.g.dart | 2 +- .../player/view/audiobook_player.dart | 35 ++++------ 4 files changed, 58 insertions(+), 53 deletions(-) diff --git a/lib/features/player/core/audiobook_player.dart b/lib/features/player/core/audiobook_player.dart index f62557e..d06ef11 100644 --- a/lib/features/player/core/audiobook_player.dart +++ b/lib/features/player/core/audiobook_player.dart @@ -22,14 +22,21 @@ Duration sumOfTracks(BookExpanded book, int? index) { /// returns the [AudioTrack] to play based on the [position] in the [book] AudioTrack getTrackToPlay(BookExpanded book, Duration position) { - var totalDuration = Duration.zero; - for (var track in book.tracks) { - totalDuration += track.duration; - if (totalDuration >= position) { - return track; - } - } - return book.tracks.last; + // var totalDuration = Duration.zero; + // for (var track in book.tracks) { + // totalDuration += track.duration; + // if (totalDuration >= position) { + // return track; + // } + // } + // 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 @@ -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 // 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 - + // initialPosition ; final trackToPlay = getTrackToPlay(book, initialPosition ?? Duration.zero); final initialIndex = book.tracks.indexOf(trackToPlay); final initialPositionInTrack = initialPosition != null - ? initialPosition - sumOfTracks(book, initialIndex - 1) + ? initialPosition - trackToPlay.startOffset : null; await setAudioSource( @@ -131,7 +138,7 @@ class AudiobookPlayer extends AudioPlayer { throw StateError('No book is set'); } - // TODO refactor this + // TODO refactor this to cover all the states return switch (playerState) { 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 @override - Future seek(Duration? position, {int? index}) async { + Future seek(Duration? positionInBook, {int? index}) async { if (_book == null) { return; } - if (position == null) { + if (positionInBook == null) { return; } - final trackToPlay = getTrackToPlay(_book!, position); - final index = _book!.tracks.indexOf(trackToPlay); - final positionInTrack = position - sumOfTracks(_book!, index - 1); + final tracks = _book!.tracks; + final trackToPlay = getTrackToPlay(_book!, positionInBook); + final index = tracks.indexOf(trackToPlay); + final positionInTrack = positionInBook - trackToPlay.startOffset; 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 // - positionStream // - bufferedPositionStream @override Stream get positionStream { + // return the positioninbook stream return super.positionStream.map((position) { if (_book == null) { 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) { 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( (element) { - return element.start <= position && element.end >= position; + return element.start <= positionInBook && element.end >= positionInBook; }, orElse: () => _book!.chapters.first, ); } - - - } diff --git a/lib/features/player/providers/currently_playing_provider.dart b/lib/features/player/providers/currently_playing_provider.dart index df6a471..68b5c10 100644 --- a/lib/features/player/providers/currently_playing_provider.dart +++ b/lib/features/player/providers/currently_playing_provider.dart @@ -14,13 +14,7 @@ BookExpanded? currentlyPlayingBook(CurrentlyPlayingBookRef ref) { @riverpod BookChapter? currentPlayingChapter(CurrentPlayingChapterRef ref) { final player = ref.watch(audiobookPlayerProvider); - // get the current timestamp - final currentTimestamp = player.position; - // get the chapter that contains the current timestamp - return player.book?.chapters.firstWhere( - (element) => - element.start <= currentTimestamp && element.end >= currentTimestamp, - ); + return player.currentChapter; } /// provides the book metadata of the currently playing book diff --git a/lib/features/player/providers/currently_playing_provider.g.dart b/lib/features/player/providers/currently_playing_provider.g.dart index 2ac37aa..11485b7 100644 --- a/lib/features/player/providers/currently_playing_provider.g.dart +++ b/lib/features/player/providers/currently_playing_provider.g.dart @@ -24,7 +24,7 @@ final currentlyPlayingBookProvider = typedef CurrentlyPlayingBookRef = AutoDisposeProviderRef; String _$currentPlayingChapterHash() => - r'562416b7e0068aaba9138cb8e0ed7a5ddba8e6c6'; + r'3a621260211cddecfd974b31d5c4820ed24b1545'; /// provided the current chapter of the book being played /// diff --git a/lib/features/player/view/audiobook_player.dart b/lib/features/player/view/audiobook_player.dart index 1d7a4a1..4b3bcb7 100644 --- a/lib/features/player/view/audiobook_player.dart +++ b/lib/features/player/view/audiobook_player.dart @@ -1,5 +1,4 @@ import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -270,6 +269,7 @@ class AudiobookChapterProgressBar extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final player = ref.watch(audiobookPlayerProvider); + final currentChapter = ref.watch(currentPlayingChapterProvider); final position = useStream( player.positionStream, initialData: const Duration(seconds: 0), @@ -278,34 +278,22 @@ class AudiobookChapterProgressBar extends HookConsumerWidget { player.bufferedPositionStream, 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 // and calculate the progress of the current chapter - final currentChapter = player.book?.chapters.firstWhereOrNull( - (element) => - (element.start <= totalProgress) && (element.end >= totalProgress), - ); final currentChapterProgress = - currentChapter == null ? null : (totalProgress - currentChapter.start); + currentChapter == null + ? null + : (player.positionInBook - currentChapter.start); final currentChapterBuffered = - currentChapter == null ? null : (totalBuffered - currentChapter.start); + currentChapter == null + ? null + : (player.bufferedPositionInBook - currentChapter.start); return ProgressBar( - progress: currentChapterProgress ?? totalProgress, + progress: + currentChapterProgress ?? position.data ?? const Duration(seconds: 0), total: currentChapter == null ? player.book?.duration ?? const Duration(seconds: 0) : currentChapter.end - currentChapter.start, @@ -316,7 +304,8 @@ class AudiobookChapterProgressBar extends HookConsumerWidget { ); }, thumbRadius: 8, - buffered: currentChapterBuffered ?? totalBuffered, + buffered: + currentChapterBuffered ?? buffered.data ?? const Duration(seconds: 0), bufferedBarColor: Theme.of(context).colorScheme.secondary, timeLabelType: TimeLabelType.remainingTime, timeLabelLocation: TimeLabelLocation.below,