refactor(api): move audiobook endpoints to new path
- Relocated all audiobook-related API routes from `/api/audio/convert/*` to `/api/audiobook/*`. - This change affects endpoints for chapter conversion, retrieval, deletion, and overall audiobook status. - Updated client-side calls in `AudiobookExportModal.tsx`, `EPUBContext.tsx`, and `PDFContext.tsx` to reflect the new API paths. - Modified API tests (`api.spec.ts`, `export.spec.ts`) to target the restructured endpoints. - The new API structure provides better organization and a clearer, more consistent interface for audiobook functionality.
This commit is contained in:
parent
733b4180eb
commit
7a29f73d07
8 changed files with 44 additions and 43 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { spawn } from 'child_process';
|
||||
import { writeFile, readFile, mkdir, unlink, readdir } from 'fs/promises';
|
||||
import { writeFile, readFile, mkdir, unlink, readdir, rm } from 'fs/promises';
|
||||
import { existsSync, createReadStream } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
|
@ -378,4 +378,31 @@ function streamFile(filePath: string, format: string) {
|
|||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
});
|
||||
}
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const bookId = request.nextUrl.searchParams.get('bookId');
|
||||
if (!bookId) {
|
||||
return NextResponse.json({ error: 'Missing bookId parameter' }, { status: 400 });
|
||||
}
|
||||
|
||||
const docstoreDir = join(process.cwd(), 'docstore');
|
||||
const intermediateDir = join(docstoreDir, `${bookId}-audiobook`);
|
||||
|
||||
// If directory doesn't exist, consider it already reset
|
||||
if (!existsSync(intermediateDir)) {
|
||||
return NextResponse.json({ success: true, existed: false });
|
||||
}
|
||||
|
||||
// Recursively delete the entire audiobook directory
|
||||
await rm(intermediateDir, { recursive: true, force: true });
|
||||
|
||||
return NextResponse.json({ success: true, existed: true });
|
||||
} catch (error) {
|
||||
console.error('Error resetting audiobook:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to reset audiobook' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { readdir, readFile, rm } from 'fs/promises';
|
||||
import { readdir, readFile } from 'fs/promises';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
|
|
@ -69,30 +69,4 @@ export async function GET(request: NextRequest) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const bookId = request.nextUrl.searchParams.get('bookId');
|
||||
if (!bookId) {
|
||||
return NextResponse.json({ error: 'Missing bookId parameter' }, { status: 400 });
|
||||
}
|
||||
|
||||
const docstoreDir = join(process.cwd(), 'docstore');
|
||||
const intermediateDir = join(docstoreDir, `${bookId}-audiobook`);
|
||||
|
||||
// If directory doesn't exist, consider it already reset
|
||||
if (!existsSync(intermediateDir)) {
|
||||
return NextResponse.json({ success: true, existed: false });
|
||||
}
|
||||
|
||||
// Recursively delete the entire audiobook directory
|
||||
await rm(intermediateDir, { recursive: true, force: true });
|
||||
|
||||
return NextResponse.json({ success: true, existed: true });
|
||||
} catch (error) {
|
||||
console.error('Error resetting audiobook:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to reset audiobook' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ export function AudiobookExportModal({
|
|||
setIsLoadingExisting(true);
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/api/audio/convert/chapters?bookId=${documentId}`);
|
||||
const response = await fetch(`/api/audiobook/status?bookId=${documentId}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.exists && data.chapters.length > 0) {
|
||||
|
|
@ -241,7 +241,7 @@ export function AudiobookExportModal({
|
|||
const performDeleteChapter = useCallback(async () => {
|
||||
if (!bookId || !pendingDeleteChapter) return;
|
||||
try {
|
||||
const response = await fetch(`/api/audio/convert/chapter?bookId=${bookId}&chapterIndex=${pendingDeleteChapter.index}`, {
|
||||
const response = await fetch(`/api/audiobook/chapter?bookId=${bookId}&chapterIndex=${pendingDeleteChapter.index}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
if (!response.ok) {
|
||||
|
|
@ -261,7 +261,7 @@ export function AudiobookExportModal({
|
|||
const targetBookId = bookId || documentId;
|
||||
if (!targetBookId) return;
|
||||
try {
|
||||
const resp = await fetch(`/api/audio/convert/chapters?bookId=${targetBookId}`, { method: 'DELETE' });
|
||||
const resp = await fetch(`/api/audiobook?bookId=${targetBookId}`, { method: 'DELETE' });
|
||||
if (!resp.ok) {
|
||||
throw new Error('Reset failed');
|
||||
}
|
||||
|
|
@ -281,7 +281,7 @@ export function AudiobookExportModal({
|
|||
if (!chapter.bookId) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/audio/convert/chapter?bookId=${chapter.bookId}&chapterIndex=${chapter.index}`);
|
||||
const response = await fetch(`/api/audiobook/chapter?bookId=${chapter.bookId}&chapterIndex=${chapter.index}`);
|
||||
if (!response.ok) throw new Error('Download failed');
|
||||
|
||||
const blob = await response.blob();
|
||||
|
|
@ -306,7 +306,7 @@ export function AudiobookExportModal({
|
|||
|
||||
setIsCombining(true);
|
||||
try {
|
||||
const response = await fetch(`/api/audio/convert?bookId=${bookId}&format=${format}`);
|
||||
const response = await fetch(`/api/audiobook?bookId=${bookId}&format=${format}`);
|
||||
if (!response.ok) throw new Error('Download failed');
|
||||
|
||||
const reader = response.body?.getReader();
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
const existingIndices = new Set<number>();
|
||||
if (bookId) {
|
||||
try {
|
||||
const existingResponse = await fetch(`/api/audio/convert/chapters?bookId=${bookId}`);
|
||||
const existingResponse = await fetch(`/api/audiobook/status?bookId=${bookId}`);
|
||||
if (existingResponse.ok) {
|
||||
const existingData = await existingResponse.json();
|
||||
if (existingData.chapters && existingData.chapters.length > 0) {
|
||||
|
|
@ -469,7 +469,7 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
// Send to server for conversion and storage
|
||||
const convertResponse = await fetch('/api/audio/convert', {
|
||||
const convertResponse = await fetch(`/api/audiobook`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -645,7 +645,7 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
// Send to server for conversion and storage
|
||||
const convertResponse = await fetch('/api/audio/convert', {
|
||||
const convertResponse = await fetch('/api/audiobook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
|
|||
const existingIndices = new Set<number>();
|
||||
if (bookId) {
|
||||
try {
|
||||
const existingResponse = await fetch(`/api/audio/convert/chapters?bookId=${bookId}`);
|
||||
const existingResponse = await fetch(`/api/audiobook/status?bookId=${bookId}`);
|
||||
if (existingResponse.ok) {
|
||||
const existingData = await existingResponse.json();
|
||||
if (existingData.chapters && existingData.chapters.length > 0) {
|
||||
|
|
@ -399,7 +399,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
// Send to server for conversion and storage
|
||||
const convertResponse = await fetch('/api/audio/convert', {
|
||||
const convertResponse = await fetch(`/api/audiobook`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -587,7 +587,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
// Send to server for conversion and storage
|
||||
const convertResponse = await fetch('/api/audio/convert', {
|
||||
const convertResponse = await fetch('/api/audiobook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ test.describe('API health checks', () => {
|
|||
expect(json.voices.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('GET /api/audio/convert/chapters returns 200 with exists flag and chapters array', async ({ request }) => {
|
||||
test('GET /api/audiobook/status returns 200 with exists flag and chapters array', async ({ request }) => {
|
||||
const bookId = `healthcheck-${Date.now()}`;
|
||||
const res = await request.get(`/api/audio/convert/chapters?bookId=${bookId}`);
|
||||
const res = await request.get(`/api/audiobook/status?bookId=${bookId}`);
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const json = await res.json();
|
||||
expect(json).toHaveProperty('exists');
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ async function getAudioDurationSeconds(filePath: string) {
|
|||
}
|
||||
|
||||
async function expectChaptersBackendState(page: Page, bookId: string) {
|
||||
const res = await page.request.get(`/api/audio/convert/chapters?bookId=${bookId}`);
|
||||
const res = await page.request.get(`/api/audiobook/status?bookId=${bookId}`);
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const json = await res.json();
|
||||
return json;
|
||||
|
|
@ -271,7 +271,7 @@ test.describe('Audiobook export', () => {
|
|||
).toBeVisible({ timeout: 60_000 });
|
||||
|
||||
// Backend should report no existing chapters for this bookId
|
||||
const res = await page.request.get(`/api/audio/convert/chapters?bookId=${bookId}`);
|
||||
const res = await page.request.get(`/api/audiobook/status?bookId=${bookId}`);
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const json = await res.json();
|
||||
expect(json.exists).toBe(false);
|
||||
|
|
|
|||
Loading…
Reference in a new issue