Redesign audio controls into a real media player
Build and Push Docker Image / build-and-push (push) Successful in 5m5s

Adds a shared AudioPlayer component with a seekable progress bar,
big play/pause button, and icon transport controls, replacing the
row of text buttons on both the book and chapter audio players.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5 committed 2026-09-23 11:59:42 -04:00
1 parent b9f9b97cba
commit f1026dc194
4 files changed
+265 -124

No files matched your search

+17
View File
@@ -8,6 +8,7 @@ export function useAudio(readerBookAbbrev, readerChapter) {
const [audioNarrator, setAudioNarrator] = useState('souer');
const [audioState, setAudioState] = useState({ status: 'idle', chapter: 0, total: 0 });
const [readerAudioState, setReaderAudioState] = useState({ status: 'idle', chapter: 0 });
const [audioProgress, setAudioProgress] = useState({ currentTime: 0, duration: 0 });
const audioRef = useRef(null);
const audioModeRef = useRef('book'); // 'book' | 'reader' — which player owns audioRef
const audioBookRef = useRef(audioBook);
@@ -26,6 +27,12 @@ export function useAudio(readerBookAbbrev, readerChapter) {
if (!url) throw new Error('Audio not available for this narrator');
if (!audioRef.current) {
audioRef.current = new Audio();
audioRef.current.addEventListener('timeupdate', () => {
setAudioProgress({ currentTime: audioRef.current.currentTime, duration: audioRef.current.duration || 0 });
});
audioRef.current.addEventListener('loadedmetadata', () => {
setAudioProgress((prev) => ({ ...prev, duration: audioRef.current.duration || 0 }));
});
audioRef.current.addEventListener('ended', () => {
if (audioModeRef.current === 'reader') {
setReaderAudioState({ status: 'idle', chapter: 0 });
@@ -67,6 +74,7 @@ export function useAudio(readerBookAbbrev, readerChapter) {
}
setAudioState({ status: 'idle', chapter: 0, total: 0 });
setReaderAudioState({ status: 'idle', chapter: 0 });
setAudioProgress({ currentTime: 0, duration: 0 });
};
const handleToggleBookAudioPause = () => {
@@ -87,6 +95,13 @@ export function useAudio(readerBookAbbrev, readerChapter) {
el.currentTime = Math.min(Math.max(el.currentTime + deltaSeconds, 0), duration);
};
const handleSeekAudioTo = (seconds) => {
if (!audioRef.current) return;
const el = audioRef.current;
const duration = Number.isFinite(el.duration) ? el.duration : 0;
el.currentTime = Math.min(Math.max(seconds, 0), duration);
};
const handleSkipBookChapter = () => {
const nextChapter = audioStateRef.current.chapter + 1;
if (nextChapter > audioStateRef.current.total) return;
@@ -144,11 +159,13 @@ export function useAudio(readerBookAbbrev, readerChapter) {
audioBook, setAudioBook,
audioNarrator, setAudioNarrator,
audioState,
audioProgress,
handlePlayBookAudio,
handleStopBookAudio,
handleToggleBookAudioPause,
handleSkipBookChapter,
handleSeekAudio,
handleSeekAudioTo,
readerAudioState,
handlePlayReaderAudio,
handleToggleReaderAudioPause,