Beranda → Blog → Suara ke teks dengan Node.js: kode siap pakai dan jebakan yang biasa
Suara ke teks dengan Node.js: kode siap pakai dan jebakan yang biasa
Di Node.js, panggilan transkripsinya sendiri sepele. Yang menjegal orang adalah semua yang mengelilinginya: handle berkas versus buffer, unggahan yang datang dari peramban, dan timeout bawaan yang diam-diam mematikan rekaman panjang.
Pemasangan dan permintaan pertama
SDK resmi bekerja tanpa perubahan — hanya alamat basis dan kunci yang berbeda:
npm install openai
import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://voicesscribe.com/v1",
apiKey: process.env.VS_KEY,
});
const r = await client.audio.transcriptions.create({
model: "whisper-1",
file: fs.createReadStream("audio.mp3"),
});
console.log(r.text);
Perhatikan createReadStream, bukan readFileSync: berkas dialirkan ke server alih-alih dimuat penuh ke memori lebih dulu. Pada server yang menangani beberapa permintaan sekaligus, perbedaan itulah seluruh profil memorinya.
Audio yang datang dari peramban
Kasus yang umum bukan berkas di disk — melainkan unggahan. Dengan Express dan multer, buffer-nya perlu dibungkus supaya SDK melihat sebuah berkas bernama:
import express from "express";
import multer from "multer";
import { toFile } from "openai/uploads";
const upload = multer({ limits: { fileSize: 25 * 1024 * 1024 } });
const app = express();
app.post("/transcribe", upload.single("audio"), async (req, res) => {
try {
const file = await toFile(req.file.buffer, req.file.originalname);
const r = await client.audio.transcriptions.create({
model: "whisper-1", file,
});
res.json({ text: r.text });
} catch (e) {
res.status(502).json({ error: String(e) });
}
});
Batas fileSize pada multer itu penting: tanpanya, peramban bisa mendorong satu gigabyte ke memori proses Anda sebelum ada yang menolaknya. Batas API adalah 25 MB, jadi menyamakannya lebih hulu membuat kegagalannya cepat dan murah.
Merekam di peramban lalu mengirimnya
Sisi peramban menghasilkan webm dari MediaRecorder, yang diterima apa adanya — tanpa konversi:
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const rec = new MediaRecorder(stream);
const chunks = [];
rec.ondataavailable = (e) => chunks.push(e.data);
rec.onstop = async () => {
const blob = new Blob(chunks, { type: "audio/webm" });
const form = new FormData();
form.append("audio", blob, "suara.webm");
const res = await fetch("/transcribe", { method: "POST", body: form });
console.log((await res.json()).text);
};
rec.start();
setTimeout(() => rec.stop(), 5000);
Jangan pernah menaruh kunci API di kode peramban. Unggahan dikirim ke endpoint Anda sendiri, dan server Anda yang menyimpan kunci — hanya itu susunan yang aman.
Timeout dan percobaan ulang
Timeout permintaan bawaan cocok untuk potongan pendek dan diam-diam mematikan yang panjang. Setel dari panjang audio yang benar-benar Anda tangani, dan biarkan SDK mengulangi kegagalan sementara:
const client = new OpenAI({
baseURL: "https://voicesscribe.com/v1",
apiKey: process.env.VS_KEY,
timeout: 5 * 60 * 1000, // lima menit: cukup untuk rekaman satu jam
maxRetries: 2, // 5xx dan galat koneksi, ditangani SDK
});
Percobaan ulang layak untuk 5xx dan galat koneksi. Galat 400 pada berkas rusak akan gagal sama persis setiap kali, jadi mengulanginya hanya membuang waktu.
Format dan penanda waktu
Takarir dan waktu segmen datang dari panggilan yang sama, hanya beda satu parameter:
// berkas takarir siap pakai sebagai string
const srt = await client.audio.transcriptions.create({
model: "whisper-1", file, response_format: "srt",
});
// segmen dengan waktu
const detail = await client.audio.transcriptions.create({
model: "whisper-1", file, response_format: "verbose_json",
});
for (const s of detail.segments) {
console.log(s.start.toFixed(1), s.text.trim());
}Galat yang layak ditangani secara eksplisit
| Kode | Artinya | Yang harus dilakukan |
|---|---|---|
| 401 | Kunci salah atau diblokir | Pastikan variabel lingkungan benar-benar sampai ke proses |
| 413 | Berkas di atas 25 MB | Kompres ulang ke mono 16 kHz, lalu potong bila perlu |
| 429 | Kena limit atau menit habis | Mundur secara eksponensial; jangan mengulang seketika |
| 502 | Node pengenalan tidak tersedia | Ulangi setelah jeda |
text kosong pada respons yang berhasil bukanlah galat — artinya tidak ada ucapan dalam rekaman itu. Perlakukan sebagai hasil normal dan jangan diulang.
Coba dengan rekaman Anda sendiri. Pendaftaran memakan waktu satu menit, dan menit gratisnya cukup untuk menilai kualitasnya.
Ambil kunci API gratisPertanyaan yang sering diajukan
Perlukah klien Node.js khusus?
Tidak, paket resmi openai bekerja tanpa perubahan. API-nya kompatibel di tingkat protokol, jadi yang berbeda dari setelan bawaan OpenAI hanya baseURL dan apiKey.
Bagaimana meneruskan berkas unggahan dari Express?
Bungkus buffer-nya dengan toFile dari openai/uploads supaya SDK melihat berkas bernama yang semestinya, lalu teruskan sebagai medan file.
Bisakah API dipanggil langsung dari peramban?
Tidak — itu akan membuka kunci Anda kepada siapa pun yang membuka halamannya. Unggah ke endpoint Anda sendiri dan simpan kuncinya di server.
Apakah webm dari MediaRecorder diterima?
Ya, webm diterima apa adanya, bersama ogg, opus, mp3, wav, dan m4a. Tidak ada langkah konversi yang diperlukan.
Kenapa rekaman panjang gagal karena timeout?
Nilai bawaan klien disetel untuk permintaan pendek. Naikkan opsi timeout agar sesuai audio terpanjang yang Anda tangani; lima menit menutupi rekaman satu jam dengan longgar.
Bacaan terkait
- Suara ke teks dengan Python: dari satu berkas ke skrip yang jalan — Transkripsi Python yang berjalan dalam sepuluh menit: pemasangan, permintaan pertama, format respons, penanganan galat, dan kesalahan yang paling banyak memakan waktu.
- Transkripsi pesan suara: WhatsApp, Telegram, dan lainnya — Cara mengubah pesan suara menjadi teks secara otomatis: format ogg/opus, menangani rekaman pendek, dan contoh kode untuk bot layanan pelanggan.
- Cara membangun pipeline transkripsi untuk arsip audio — Mentranskrip ribuan rekaman tanpa kehilangan satu pun: antrean kerja, konkurensi, percobaan ulang, melanjutkan setelah proses mati, dan menjaga tagihan tetap terkendali.