Skip to content

Commit 43a13d5

Browse files
sweetmantechclaude
andcommitted
fix: decode/sanitize Slack filenames, add debug logging to transcribeSong
- Decode URI components and replace spaces with hyphens in audio filenames - Add detailed logging around fal.storage.upload and fal-ai/whisper calls to diagnose Bad Request errors (logs filename, size, audioUrl, error) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d974928 commit 43a13d5

File tree

3 files changed

+60
-13
lines changed

3 files changed

+60
-13
lines changed

src/content/__tests__/selectAttachedAudioClip.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,23 @@ describe("selectAttachedAudioClip", () => {
8585
expect(result.songTitle).toBe("my-track");
8686
});
8787

88+
it("decodes URL-encoded filenames from Slack URLs", async () => {
89+
vi.mocked(transcribeSong).mockResolvedValue({
90+
title: "Singin in the Rain 2",
91+
fullLyrics: "",
92+
segments: [],
93+
});
94+
vi.mocked(analyzeClips).mockResolvedValue([]);
95+
96+
const result = await selectAttachedAudioClip({
97+
audioUrl: "https://blob.vercel-storage.com/content-attachments/audio/1774985247995-Singin%20in%20the%20Rain%202.mp3",
98+
lipsync: false,
99+
});
100+
101+
expect(result.songFilename).toBe("1774985247995-Singin-in-the-Rain-2.mp3");
102+
expect(result.songTitle).toBe("1774985247995-Singin-in-the-Rain-2");
103+
});
104+
88105
it("transcribes the downloaded audio", async () => {
89106
vi.mocked(transcribeSong).mockResolvedValue({
90107
title: "song",

src/content/selectAttachedAudioClip.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ export async function selectAttachedAudioClip({
3030
const arrayBuffer = await response.arrayBuffer();
3131
const songBuffer = Buffer.from(arrayBuffer);
3232

33-
// Derive filename from URL
33+
// Derive filename from URL (decode %20, replace spaces for fal.ai compat)
3434
const urlPath = new URL(audioUrl).pathname;
35-
const songFilename = urlPath.split("/").pop() ?? "attached-audio.mp3";
35+
const songFilename = decodeURIComponent(urlPath.split("/").pop() ?? "attached-audio.mp3").replace(/\s+/g, "-");
3636
const songTitle = songFilename.replace(/\.(mp3|wav|m4a|ogg|aac)$/i, "");
3737

3838
logStep("Attached audio downloaded", { songTitle, sizeBytes: songBuffer.byteLength });

src/content/transcribeSong.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,52 @@ export async function transcribeSong(
2525
songBuffer: Buffer,
2626
songFilename: string,
2727
): Promise<SongLyrics> {
28-
logger.log("Transcribing song", { filename: songFilename });
28+
logger.log("Transcribing song", {
29+
filename: songFilename,
30+
bufferSize: songBuffer.byteLength,
31+
});
2932

3033
// Upload audio to fal storage
3134
const file = new File([songBuffer], songFilename, { type: "audio/mpeg" });
32-
const audioUrl = await fal.storage.upload(file);
35+
logger.log("Uploading audio to fal storage", {
36+
fileName: file.name,
37+
fileSize: file.size,
38+
fileType: file.type,
39+
});
40+
41+
let audioUrl: string;
42+
try {
43+
audioUrl = await fal.storage.upload(file);
44+
logger.log("Audio uploaded to fal storage", { audioUrl });
45+
} catch (uploadError) {
46+
logger.log("fal.storage.upload failed", {
47+
error: String(uploadError),
48+
message: (uploadError as Error).message,
49+
});
50+
throw uploadError;
51+
}
3352

3453
// Transcribe with Whisper
35-
const result = await fal.subscribe("fal-ai/whisper" as string, {
36-
input: {
37-
audio_url: audioUrl,
38-
task: "transcribe",
39-
chunk_level: "word",
40-
language: "en",
41-
},
42-
logs: true,
43-
});
54+
let result;
55+
try {
56+
result = await fal.subscribe("fal-ai/whisper" as string, {
57+
input: {
58+
audio_url: audioUrl,
59+
task: "transcribe",
60+
chunk_level: "word",
61+
language: "en",
62+
},
63+
logs: true,
64+
});
65+
} catch (whisperError) {
66+
logger.log("fal-ai/whisper failed", {
67+
error: String(whisperError),
68+
message: (whisperError as Error).message,
69+
audioUrl,
70+
filename: songFilename,
71+
});
72+
throw whisperError;
73+
}
4474

4575
const data = result.data as unknown as {
4676
text?: string;

0 commit comments

Comments
 (0)