The Reader's player follows the translation on screen: ESV plays its own recording via a server route that resolves the ESV API's audio redirect; other translations keep the BSB narrators. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e50a73d351
commit
3279dddae5
4 files changed
+51
-13
No files matched your search
+25
-10
@@ -1,9 +1,9 @@
|
||||
// Chapter audio playback. The Home page's whole-book player and the Reader's
|
||||
// chapter player share one <audio> element; audioModeRef tracks which owns it.
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { bookOptions } from '../utils/bibleUtils.jsx';
|
||||
import { bookOptions, CHAPTER_COUNTS } from '../utils/bibleUtils.jsx';
|
||||
|
||||
export function useAudio(readerBookAbbrev, readerChapter) {
|
||||
export function useAudio(readerBookAbbrev, readerChapter, readerTranslation = 'BSB') {
|
||||
const [audioBook, setAudioBook] = useState(bookOptions[0].abbrev);
|
||||
const [audioNarrator, setAudioNarrator] = useState('souer');
|
||||
const [audioState, setAudioState] = useState({ status: 'idle', chapter: 0, total: 0 });
|
||||
@@ -17,13 +17,28 @@ export function useAudio(readerBookAbbrev, readerChapter) {
|
||||
audioBookRef.current = audioBook;
|
||||
audioNarratorRef.current = audioNarrator;
|
||||
audioStateRef.current = audioState;
|
||||
// The Reader's player follows the translation on screen: ESV has its own
|
||||
// recording; every other translation falls back to the BSB narrators.
|
||||
const readerTranslationRef = useRef(readerTranslation);
|
||||
readerTranslationRef.current = readerTranslation;
|
||||
const readerAudioSource = () => (readerTranslationRef.current === 'ESV' ? 'ESV' : 'BSB');
|
||||
|
||||
const playAudioChapter = async (abbrev, chapterNum, narrator) => {
|
||||
const res = await fetch(`https://bible.helloao.org/api/BSB/${abbrev}/${chapterNum}.json`);
|
||||
if (!res.ok) throw new Error('Failed to load chapter audio');
|
||||
const data = await res.json();
|
||||
const total = data.book?.numberOfChapters ?? chapterNum;
|
||||
const url = data.thisChapterAudioLinks?.[narrator];
|
||||
const source = audioModeRef.current === 'reader' ? readerAudioSource() : 'BSB';
|
||||
let url;
|
||||
let total;
|
||||
if (source === 'ESV') {
|
||||
const res = await fetch(`/api/bible/ESV/audio/${abbrev}/${chapterNum}`, { credentials: 'include' });
|
||||
if (!res.ok) throw new Error('Failed to load ESV audio');
|
||||
url = (await res.json()).url;
|
||||
total = CHAPTER_COUNTS[abbrev] ?? chapterNum;
|
||||
} else {
|
||||
const res = await fetch(`https://bible.helloao.org/api/BSB/${abbrev}/${chapterNum}.json`);
|
||||
if (!res.ok) throw new Error('Failed to load chapter audio');
|
||||
const data = await res.json();
|
||||
total = data.book?.numberOfChapters ?? chapterNum;
|
||||
url = data.thisChapterAudioLinks?.[narrator];
|
||||
}
|
||||
if (!url) throw new Error('Audio not available for this narrator');
|
||||
if (!audioRef.current) {
|
||||
audioRef.current = new Audio();
|
||||
@@ -52,7 +67,7 @@ export function useAudio(readerBookAbbrev, readerChapter) {
|
||||
audioEl.src = url;
|
||||
await audioEl.play();
|
||||
if (audioModeRef.current === 'reader') {
|
||||
setReaderAudioState({ status: 'playing', chapter: chapterNum });
|
||||
setReaderAudioState({ status: 'playing', chapter: chapterNum, source });
|
||||
} else {
|
||||
setAudioState({ status: 'playing', chapter: chapterNum, total });
|
||||
}
|
||||
@@ -149,7 +164,7 @@ export function useAudio(readerBookAbbrev, readerChapter) {
|
||||
useEffect(() => {
|
||||
if (audioModeRef.current !== 'reader') return;
|
||||
if (readerAudioState.status !== 'playing' && readerAudioState.status !== 'paused') return;
|
||||
if (readerAudioState.chapter === readerChapter) return;
|
||||
if (readerAudioState.chapter === readerChapter && readerAudioState.source === readerAudioSource()) return;
|
||||
const wasPaused = readerAudioState.status === 'paused';
|
||||
playAudioChapter(readerBookAbbrev, readerChapter, audioNarrator)
|
||||
.then(() => {
|
||||
@@ -160,7 +175,7 @@ export function useAudio(readerBookAbbrev, readerChapter) {
|
||||
})
|
||||
.catch(() => setReaderAudioState({ status: 'error', chapter: 0 }));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [readerBookAbbrev, readerChapter]);
|
||||
}, [readerBookAbbrev, readerChapter, readerTranslation]);
|
||||
|
||||
useEffect(() => () => {
|
||||
if (audioRef.current) {
|
||||
|
||||
Reference in new issue
Block a user