"use client"; import { createFalClient } from "@fal-ai/client"; import { useCallback, useMemo, useState } from "react"; const fal = createFalClient({ // credentials: 'FAL_KEY_ID:FAL_KEY_SECRET', proxyUrl: "/api/fal/proxy", }); type ErrorProps = { error: any; }; function Error(props: ErrorProps) { if (!props.error) { return null; } return (
Error {props.error.message}
); } type RecorderOptions = { maxDuration?: number; }; function useMediaRecorder({ maxDuration = 10000 }: RecorderOptions = {}) { const [isRecording, setIsRecording] = useState(false); const [mediaRecorder, setMediaRecorder] = useState( null, ); const record = useCallback(async () => { setIsRecording(true); const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const audioChunks: BlobPart[] = []; const recorder = new MediaRecorder(stream); setMediaRecorder(recorder); return new Promise((resolve, reject) => { try { recorder.addEventListener("dataavailable", (event) => { audioChunks.push(event.data); }); recorder.addEventListener("stop", async () => { const fileOptions = { type: "audio/wav" }; const audioBlob = new Blob(audioChunks, fileOptions); const audioFile = new File( [audioBlob], `recording_${Date.now()}.wav`, fileOptions, ); setIsRecording(false); resolve(audioFile); }); setTimeout(() => { recorder.stop(); recorder.stream.getTracks().forEach((track) => track.stop()); }, maxDuration); recorder.start(); } catch (error) { reject(error); } }); }, [maxDuration]); const stopRecording = useCallback(() => { setIsRecording(false); mediaRecorder?.stop(); mediaRecorder?.stream.getTracks().forEach((track) => track.stop()); }, [mediaRecorder]); return { record, stopRecording, isRecording }; } export default function WhisperDemo() { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [logs, setLogs] = useState([]); const [audioFile, setAudioFile] = useState(null); const [result, setResult] = useState(null); // eslint-disable-line @typescript-eslint/no-explicit-any const [elapsedTime, setElapsedTime] = useState(0); const { record, stopRecording, isRecording } = useMediaRecorder(); const reset = () => { setLoading(false); setError(null); setLogs([]); setElapsedTime(0); setResult(null); }; const audioFileLocalUrl = useMemo(() => { if (!audioFile) { return null; } return URL.createObjectURL(audioFile); }, [audioFile]); const transcribeAudio = async (audioFile: File) => { reset(); setLoading(true); const start = Date.now(); try { const result = await fal.subscribe("fal-ai/whisper", { input: { file_name: "recording.wav", audio_url: audioFile, }, logs: true, onQueueUpdate(update) { setElapsedTime(Date.now() - start); if ( update.status === "IN_PROGRESS" || update.status === "COMPLETED" ) { setLogs((update.logs || []).map((log) => log.message)); } }, }); setResult(result); } catch (error: any) { setError(error); } finally { setLoading(false); setElapsedTime(Date.now() - start); } }; return (

Hello fal and{" "} whisper

{audioFileLocalUrl && (
)}

JSON Result

{`Elapsed Time (seconds): ${(elapsedTime / 1000).toFixed(2)}`}

              {result
                ? JSON.stringify(result, null, 2)
                : "// result pending..."}
            

Logs

              {logs.filter(Boolean).join("\n")}
            
); }