fix: replace speechSynthesis announce with real audio clip via /api/tts-announce
speechSynthesis is silently muted on iOS Safari and Chrome Android after the audio session ends (onended), so chapter announcements never played. Fix: - Add GET /api/tts-announce backend endpoint: streams a short TTS clip for arbitrary text without MinIO caching (backend/internal/backend/) - Add GET /api/announce SvelteKit proxy route (no paywall) - Add announceNavigatePending/announcePendingSlug/announcePendingChapter to AudioStore - Rewrite onended announce branch: sets audioStore.audioUrl to the announcement clip URL so the persistent <audio> element plays it; the next onended detects announceNavigatePending and navigates - 10s safety timeout in case the clip fails to load/end
This commit is contained in:
39
ui/src/routes/api/announce/+server.ts
Normal file
39
ui/src/routes/api/announce/+server.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { backendFetch } from '$lib/server/scraper';
|
||||
|
||||
/**
|
||||
* GET /api/announce?text=...&voice=...&format=...
|
||||
*
|
||||
* Thin proxy to backend GET /api/tts-announce.
|
||||
* No paywall — this is a short announcement clip (a few words), not chapter audio.
|
||||
* No MinIO caching — the backend streams the clip directly.
|
||||
*/
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
const text = url.searchParams.get('text') ?? '';
|
||||
if (!text) error(400, 'text is required');
|
||||
|
||||
const qs = new URLSearchParams();
|
||||
qs.set('text', text);
|
||||
|
||||
const voice = url.searchParams.get('voice');
|
||||
if (voice) qs.set('voice', voice);
|
||||
|
||||
const format = url.searchParams.get('format') ?? 'mp3';
|
||||
qs.set('format', format);
|
||||
|
||||
const backendRes = await backendFetch(`/api/tts-announce?${qs}`);
|
||||
|
||||
if (!backendRes.ok) {
|
||||
error(backendRes.status as Parameters<typeof error>[0], 'TTS announce failed');
|
||||
}
|
||||
|
||||
return new Response(backendRes.body, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': backendRes.headers.get('Content-Type') ?? 'audio/mpeg',
|
||||
'Cache-Control': 'no-store',
|
||||
'X-Accel-Buffering': 'no'
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user