Export to m4b from WAV efficiently

This commit is contained in:
Richard Roberson 2025-02-25 20:01:15 -07:00
parent 79d2e7f613
commit 4a2d75e155
4 changed files with 10 additions and 23 deletions

View file

@ -87,26 +87,16 @@ export async function POST(request: NextRequest) {
await mkdir(intermediateDir);
}
// First, write each chapter to a temporary file and get its duration
// Process each chapter - no need for initial conversion since input is WAV
const chapterFiles: { path: string; title: string; duration: number }[] = [];
let currentTime = 0;
for (let i = 0; i < data.chapters.length; i++) {
const chapter = data.chapters[i];
const inputPath = join(intermediateDir, `${i}-input.aac`);
const outputPath = join(intermediateDir, `${i}.wav`);
// Write the chapter audio to a temp file
await writeFile(inputPath, Buffer.from(new Uint8Array(chapter.buffer)));
// Convert to WAV with consistent format (this helps with timestamp issues)
await runFFmpeg([
'-i', inputPath,
'-acodec', 'pcm_s16le',
'-ar', '44100',
'-ac', '2',
outputPath
]);
// Write the chapter audio directly since it's already WAV
await writeFile(outputPath, Buffer.from(new Uint8Array(chapter.buffer)));
// Get the duration of this chapter
const duration = await getAudioDuration(outputPath);
@ -116,9 +106,6 @@ export async function POST(request: NextRequest) {
title: chapter.title,
duration
});
// Clean up input file
await unlink(inputPath).catch(console.error);
}
// Create chapter metadata file
@ -129,7 +116,7 @@ export async function POST(request: NextRequest) {
);
// Calculate chapter timings based on actual durations
chapterFiles.forEach((chapter, index) => {
chapterFiles.forEach((chapter) => {
const startMs = Math.floor(currentTime * 1000);
currentTime += chapter.duration;
const endMs = Math.floor(currentTime * 1000);

View file

@ -29,7 +29,8 @@ export async function POST(req: NextRequest) {
voice: voice as "alloy",
input: text,
speed: speed,
response_format: format === 'aac' ? 'aac' : 'mp3',
// Use wav format for audiobook generation to avoid initial conversion
response_format: format === 'audiobook' ? 'wav' : (format === 'aac' ? 'aac' : 'mp3'),
}, { signal: req.signal }); // Pass the abort signal to OpenAI client
// Get the audio data as array buffer
@ -37,7 +38,7 @@ export async function POST(req: NextRequest) {
const stream = response.body;
// Return audio data with appropriate headers
const contentType = format === 'aac' ? 'audio/aac' : 'audio/mpeg';
const contentType = format === 'audiobook' ? 'audio/wav' : (format === 'aac' ? 'audio/aac' : 'audio/mpeg');
return new NextResponse(stream, {
headers: {
'Content-Type': contentType

View file

@ -224,7 +224,7 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
text: text.trim(),
voice: voice,
speed: voiceSpeed,
format: 'aac'
format: 'audiobook' // Request WAV format directly
}),
signal
});
@ -244,7 +244,7 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
let spineIndex = processedSections;
let currentSpineHref: string | undefined;
spine.each((item: any) => {
spine.each((item: SpineItem) => {
if (spineIndex === 0) {
currentSpineHref = item.href;
}

View file

@ -36,7 +36,6 @@ import {
} from '@/utils/pdf';
import type { PDFDocumentProxy } from 'pdfjs-dist';
import { useParams } from 'next/navigation';
/**
* Interface defining all available methods and properties in the PDF context
@ -234,7 +233,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
text: text.trim(),
voice: voice,
speed: voiceSpeed,
format: 'aac'
format: 'audiobook' // Request WAV format directly
}),
signal
});