fix: transcription Content-Type + language detection for Deepgram/HuggingFace

- Add resolveAudioContentType() to map video/* MIME to audio/* (fixes .mp4 uploads returning 'no speech detected')
- Add detect_language=true for Deepgram auto-language detection (fixes non-English audio)
- Add punctuate=true for better output quality
- Forward language form param to Deepgram when provided
- Apply same Content-Type fix to HuggingFace handler
This commit is contained in:
diegosouzapw
2026-03-21 10:38:57 -03:00
parent 03f2ef1e2b
commit 538a3e855c
2 changed files with 60 additions and 5 deletions

View File

@@ -65,13 +65,67 @@ function getUploadedFileName(file: Blob & { name?: unknown }): string {
return typeof file.name === "string" && file.name.length > 0 ? file.name : "audio.wav";
}
/**
* Infer a suitable Content-Type for Deepgram from the browser-provided MIME
* type and the original filename. Deepgram accepts `audio/*` and many raw
* formats, but `video/*` causes it to silently fail with "no speech detected".
*
* Strategy:
* 1. If the browser says `audio/*`, keep it as-is.
* 2. If it's `video/*` (e.g. `.mp4`), remap to the audio equivalent so
* Deepgram extracts the audio track. `.mp4` → `audio/mp4`, etc.
* 3. Fall back to `application/octet-stream` which tells Deepgram to
* auto-detect from the raw bytes (most reliable for unknown formats).
*/
function resolveAudioContentType(file: Blob & { name?: unknown }): string {
const browserType = (file.type || "").toLowerCase();
const fileName = typeof file.name === "string" ? file.name.toLowerCase() : "";
// 1) Browser already says it's audio — trust it
if (browserType.startsWith("audio/")) return browserType;
// 2) Derive from file extension (covers video/* and empty MIME)
const ext = fileName.includes(".") ? fileName.split(".").pop() : "";
const EXT_TO_MIME: Record<string, string> = {
mp3: "audio/mpeg",
mp4: "audio/mp4",
m4a: "audio/mp4",
wav: "audio/wav",
ogg: "audio/ogg",
flac: "audio/flac",
webm: "audio/webm",
aac: "audio/aac",
wma: "audio/x-ms-wma",
opus: "audio/opus",
};
if (ext && EXT_TO_MIME[ext]) return EXT_TO_MIME[ext];
// 3) Fallback — let Deepgram auto-detect from raw bytes
return "application/octet-stream";
}
/**
* Handle Deepgram transcription (raw binary audio, model via query param)
*/
async function handleDeepgramTranscription(providerConfig, file, modelId, token) {
async function handleDeepgramTranscription(
providerConfig,
file,
modelId,
token,
formData?: FormData
) {
const url = new URL(providerConfig.baseUrl);
url.searchParams.set("model", modelId);
url.searchParams.set("smart_format", "true");
url.searchParams.set("punctuate", "true");
// Language: if caller specified one, use it; otherwise let Deepgram auto-detect
const langParam = formData?.get("language");
if (typeof langParam === "string" && langParam.trim()) {
url.searchParams.set("language", langParam.trim());
} else {
url.searchParams.set("detect_language", "true");
}
const arrayBuffer = await file.arrayBuffer();
@@ -79,7 +133,7 @@ async function handleDeepgramTranscription(providerConfig, file, modelId, token)
method: "POST",
headers: {
...buildAuthHeaders(providerConfig, token),
"Content-Type": file.type || "audio/wav",
"Content-Type": resolveAudioContentType(file),
},
body: arrayBuffer,
});
@@ -212,7 +266,7 @@ async function handleHuggingFaceTranscription(providerConfig, file, modelId, tok
method: "POST",
headers: {
...buildAuthHeaders(providerConfig, token),
"Content-Type": file.type || "audio/wav",
"Content-Type": resolveAudioContentType(file),
},
body: arrayBuffer,
});
@@ -283,7 +337,7 @@ export async function handleAudioTranscription({
// Route to provider-specific handler
if (providerConfig.format === "deepgram") {
return handleDeepgramTranscription(providerConfig, file, modelId, token);
return handleDeepgramTranscription(providerConfig, file, modelId, token, formData);
}
if (providerConfig.format === "assemblyai") {

View File

@@ -34,7 +34,8 @@
"**/*.js",
"**/*.jsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
".next/dev/types/**/*.ts",
".next/dev/dev/types/**/*.ts"
],
"exclude": [
"node_modules",