Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ app.route("/api", createQuizRoutes(booksDir))
app.route("/api", createPackageRoutes(booksDir, webAssetsDir, configPath, taskService))
app.route("/api", createPromptRoutes(promptsDir, booksDir))
app.route("/api", createTextCatalogRoutes(booksDir))
app.route("/api", createTTSRoutes(booksDir, configPath))
app.route("/api", createTTSRoutes(booksDir, configPath, taskService))
app.route(
"/api",
createStageRoutes(stageService, eventBus, booksDir, promptsDir, webAssetsDir, configPath)
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/routes/books.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,8 @@ describe("POST /books/:label/stages/run", () => {
"X-Gemini-API-Key": "gm-test",
},
body: JSON.stringify({
fromStage: "text-and-speech",
toStage: "text-and-speech",
fromStage: "translate",
toStage: "speech",
}),
})

Expand Down
16 changes: 8 additions & 8 deletions apps/api/src/routes/pages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ describe("Page routes", () => {
}
}

/** Assert that all caption + text-and-speech node data and step_runs were cleared. */
/** Assert that all caption + translate/speech node data and step_runs were cleared. */
function expectAllDownstreamCleared(dir: string, bookLabel: string) {
const s = createBookStorage(bookLabel, dir)
try {
Expand All @@ -689,7 +689,7 @@ describe("Page routes", () => {
}
}

/** Assert that text-and-speech (but NOT image-captioning) node data and step_runs were cleared. */
/** Assert that translate/speech (but NOT image-captioning) node data and step_runs were cleared. */
function expectTextAndSpeechCleared(dir: string, bookLabel: string) {
const s = createBookStorage(bookLabel, dir)
try {
Expand All @@ -711,7 +711,7 @@ describe("Page routes", () => {
}

describe("PUT /api/books/:label/pages/:pageId/sectioning clears downstream", () => {
it("clears caption + text-and-speech data on sectioning save", async () => {
it("clears caption + translate/speech data on sectioning save", async () => {
seedDownstreamData(tmpDir, label)

const data = {
Expand Down Expand Up @@ -748,7 +748,7 @@ describe("Page routes", () => {
})

describe("PUT /api/books/:label/pages/:pageId/rendering clears downstream", () => {
it("clears caption + text-and-speech data on rendering save", async () => {
it("clears caption + translate/speech data on rendering save", async () => {
seedDownstreamData(tmpDir, label)

const data = {
Expand All @@ -775,7 +775,7 @@ describe("Page routes", () => {
})

describe("POST clone clears downstream", () => {
it("clears caption + text-and-speech data on section clone", async () => {
it("clears caption + translate/speech data on section clone", async () => {
seedDownstreamData(tmpDir, label)

const res = await app.request(
Expand All @@ -789,7 +789,7 @@ describe("Page routes", () => {
})

describe("POST delete clears downstream", () => {
it("clears caption + text-and-speech data on section delete", async () => {
it("clears caption + translate/speech data on section delete", async () => {
// Need at least 2 sections so delete is valid
const s = createBookStorage(label, tmpDir)
try {
Expand Down Expand Up @@ -839,7 +839,7 @@ describe("Page routes", () => {
})

describe("POST crop (images) clears downstream", () => {
it("clears caption + text-and-speech data on image crop", async () => {
it("clears caption + translate/speech data on image crop", async () => {
seedDownstreamData(tmpDir, label)

// Minimal valid PNG (1x1 pixel)
Expand Down Expand Up @@ -867,7 +867,7 @@ describe("Page routes", () => {
})
})

describe("PUT image-captioning clears text-and-speech downstream", () => {
describe("PUT image-captioning clears translate/speech downstream", () => {
it("clears text-catalog/translations/TTS but keeps image-captioning", async () => {
seedDownstreamData(tmpDir, label)

Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/routes/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,9 @@ async function executeAiImageGeneration(params: AiImageGenParams): Promise<{
}
}

/** Clear caption + downstream text-and-speech data when images change. */
/** Clear caption + downstream translate/speech data when images change. */
function clearCaptionData(storage: Storage): void {
storage.clearNodesByType(["image-captioning", "text-catalog", "text-catalog-translation", "tts"])
storage.clearNodesByType(["image-captioning", "text-catalog", "text-catalog-translation", "tts", "tts-timestamps"])
storage.clearStepRuns(["image-captioning", "text-catalog", "catalog-translation", "tts"])
}

Expand Down Expand Up @@ -736,7 +736,7 @@ export function createPageRoutes(

const version = storage.putNodeData("image-captioning", pageId, parsed.data)
// Caption change cascades to text-catalog/translations/TTS
storage.clearNodesByType(["text-catalog", "text-catalog-translation", "tts"])
storage.clearNodesByType(["text-catalog", "text-catalog-translation", "tts", "tts-timestamps"])
storage.clearStepRuns(["text-catalog", "catalog-translation", "tts"])
return c.json({ version })
} finally {
Expand Down
54 changes: 54 additions & 0 deletions apps/api/src/routes/tts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,57 @@ describe("POST /books/:label/tts/generate-one", () => {
)
})
})

describe("DELETE /books/:label/tts", () => {
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "adt-tts-route-"))
configPath = path.join(tmpDir, "config.yaml")
writeConfig()
})

afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true })
tmpDir = ""
configPath = ""
})

it("clears both tts and tts-timestamps data", async () => {
const label = "delete-tts"
seedBook(label)

// Seed TTS and tts-timestamps data
const storage = createBookStorage(label, tmpDir)
try {
storage.putNodeData("tts", "en", {
entries: [{ textId: "pg001_t001", fileName: "pg001_t001.wav" }],
generatedAt: new Date().toISOString(),
})
storage.putNodeData("tts-timestamps", "en", {
entries: {
pg001_t001: {
textId: "pg001_t001",
language: "en",
words: [{ word: "Hello", start: 0, end: 0.5 }],
duration: 0.5,
},
},
generatedAt: new Date().toISOString(),
})
} finally {
storage.close()
}

const app = createTTSRoutes(tmpDir, configPath)
const res = await app.request(`/books/${label}/tts`, { method: "DELETE" })

expect(res.status).toBe(200)

const after = createBookStorage(label, tmpDir)
try {
expect(after.getLatestNodeData("tts", "en")).toBeNull()
expect(after.getLatestNodeData("tts-timestamps", "en")).toBeNull()
} finally {
after.close()
}
})
})
Loading
Loading