diff --git a/app/src/lib/hooks/useHistory.ts b/app/src/lib/hooks/useHistory.ts index e6f4aa7a..51983339 100644 --- a/app/src/lib/hooks/useHistory.ts +++ b/app/src/lib/hooks/useHistory.ts @@ -47,12 +47,14 @@ export function useExportGeneration() { mutationFn: async ({ generationId, text }: { generationId: string; text: string }) => { const blob = await apiClient.exportGeneration(generationId); - // Create safe filename from text + // Create safe filename from text. Append a short id so exports of + // similarly-worded generations don't collide on the same filename + // (the first 30 chars are frequently identical). const safeText = text .substring(0, 30) .replace(/[^a-z0-9]/gi, '-') .toLowerCase(); - const filename = `generation-${safeText}.voicebox.zip`; + const filename = `generation-${safeText}-${generationId.substring(0, 8)}.voicebox.zip`; await platform.filesystem.saveFile(filename, blob, [ { @@ -73,12 +75,14 @@ export function useExportGenerationAudio() { mutationFn: async ({ generationId, text }: { generationId: string; text: string }) => { const blob = await apiClient.exportGenerationAudio(generationId); - // Create safe filename from text + // Create safe filename from text. Append a short id so exports of + // similarly-worded generations don't collide on the same filename + // (the first 30 chars are frequently identical). const safeText = text .substring(0, 30) .replace(/[^a-z0-9]/gi, '-') .toLowerCase(); - const filename = `${safeText}.wav`; + const filename = `${safeText}-${generationId.substring(0, 8)}.wav`; await platform.filesystem.saveFile(filename, blob, [ { diff --git a/backend/routes/history.py b/backend/routes/history.py index d5aa0533..32b7a542 100644 --- a/backend/routes/history.py +++ b/backend/routes/history.py @@ -151,7 +151,9 @@ async def export_generation( safe_text = "".join(c for c in generation.text[:30] if c.isalnum() or c in (" ", "-", "_")).strip() if not safe_text: safe_text = "generation" - filename = f"generation-{safe_text}.voicebox.zip" + # Append a short id so exports of similarly-worded generations don't collide + # on the same filename (the first 30 chars are frequently identical). + filename = f"generation-{safe_text}-{generation_id[:8]}.voicebox.zip" return StreamingResponse( io.BytesIO(zip_bytes), @@ -180,7 +182,9 @@ async def export_generation_audio( safe_text = "".join(c for c in generation.text[:30] if c.isalnum() or c in (" ", "-", "_")).strip() if not safe_text: safe_text = "generation" - filename = f"{safe_text}.wav" + # Append a short id so exports of similarly-worded generations don't collide + # on the same filename (the first 30 chars are frequently identical). + filename = f"{safe_text}-{generation_id[:8]}.wav" return FileResponse( audio_path,