电子书馆增加听书功能

This commit is contained in:
mtvpls
2026-05-04 14:50:28 +08:00
parent 038abff091
commit da7b01bc35
11 changed files with 1290 additions and 54 deletions
+52
View File
@@ -0,0 +1,52 @@
import { NextRequest, NextResponse } from 'next/server';
import { getBookTtsConfig, synthesizeBookTts } from '@/lib/book-tts';
import { getAuthorizedBooksUsername } from '../../_utils';
export const runtime = 'nodejs';
type SynthesizePayload = {
sourceId?: string;
bookId?: string;
chapterHref?: string;
text?: string;
voice?: string;
rate?: string;
pitch?: string;
volume?: string;
};
export async function POST(request: NextRequest) {
const username = await getAuthorizedBooksUsername(request);
if (username instanceof NextResponse) return username;
try {
const config = await getBookTtsConfig();
const payload = await request.json() as SynthesizePayload;
if (!payload.sourceId?.trim() || !payload.bookId?.trim() || !payload.chapterHref?.trim()) {
return NextResponse.json({ error: '缺少 sourceId / bookId / chapterHref' }, { status: 400 });
}
const result = await synthesizeBookTts({
sourceId: payload.sourceId.trim(),
bookId: payload.bookId.trim(),
chapterHref: payload.chapterHref.trim(),
text: payload.text?.trim() || '',
voice: payload.voice?.trim() || config.defaultVoice,
rate: payload.rate?.trim() || config.defaultRate,
pitch: payload.pitch?.trim() || config.defaultPitch,
volume: payload.volume?.trim() || config.defaultVolume,
});
return NextResponse.json({
audioBase64: result.audioBuffer.toString('base64'),
mimeType: result.mimeType,
boundaries: result.boundaries,
cacheKey: result.cacheKey,
cacheHit: result.cacheHit,
});
} catch (error) {
return NextResponse.json({ error: (error as Error).message || '语音合成失败' }, { status: 500 });
}
}
+31
View File
@@ -0,0 +1,31 @@
import { NextRequest, NextResponse } from 'next/server';
import { getBookTtsConfig, listBookTtsVoices } from '@/lib/book-tts';
import { getAuthorizedBooksUsername } from '../../_utils';
export const runtime = 'nodejs';
export async function GET(request: NextRequest) {
const username = await getAuthorizedBooksUsername(request);
if (username instanceof NextResponse) return username;
try {
const config = await getBookTtsConfig();
const voices = await listBookTtsVoices();
return NextResponse.json({
voices,
defaults: {
voice: config.defaultVoice,
rate: config.defaultRate,
pitch: config.defaultPitch,
volume: config.defaultVolume,
maxCharsPerChunk: config.maxCharsPerChunk,
prefetchChunks: config.prefetchChunks,
maxTextLengthPerRequest: config.maxTextLengthPerRequest,
},
});
} catch (error) {
return NextResponse.json({ error: (error as Error).message || '获取音色列表失败' }, { status: 500 });
}
}