BerandaBlog → 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

Diterbitkan 2026-08-18 · 6 menit baca

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

KodeArtinyaYang harus dilakukan
401Kunci salah atau diblokirPastikan variabel lingkungan benar-benar sampai ke proses
413Berkas di atas 25 MBKompres ulang ke mono 16 kHz, lalu potong bila perlu
429Kena limit atau menit habisMundur secara eksponensial; jangan mengulang seketika
502Node pengenalan tidak tersediaUlangi 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 gratis

Pertanyaan 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