# SDK cho Node.js

> Tích hợp @transcript1/sdk vào code server để lấy transcript từ link hoặc file, tải văn bản hay phụ đề, theo dõi tiến độ và xử lý lỗi.

Canonical: https://transcript1.com/vi/docs/sdk
Language: vi
Last updated: 2026-10-16

Với `@transcript1/sdk`, bạn có thể gửi link, tải file lên và chờ transcript ngay trong code TypeScript hoặc JavaScript chạy trên server.

Link mạng xã hội cần SDK từ 1.1.0. Bản 1.0.1 chỉ nhận YouTube và chặn các link mới ngay ở client, chưa gửi đến API. Cập nhật bằng `npm i @transcript1/sdk@latest`.

Mốc trên áp dụng cho sáu nền tảng ban đầu. Với Twitch, Dailymotion, Pinterest, LinkedIn và Snapchat, cần bản SDK đã bổ sung nền tảng tương ứng. Bản cũ có thể từ chối link trước khi gọi API.

## Cài đặt

```bash
npm i @transcript1/sdk
```

Code mẫu dùng ES modules. Lưu thành file `.mjs`, hoặc dùng `.js` với `"type": "module"` trong `package.json`. Nếu chạy Node 20.19+, bạn cũng có thể dùng `require()`, chẳng hạn `require('@transcript1/sdk')`.

## Cấu hình khóa API

Lấy khóa từ [“API keys”](https://transcript1.com/dashboard/account/tokens), rồi đặt biến môi trường `TRANSCRIPT1_API_KEY` trên server. Nếu muốn truyền trực tiếp, dùng `apiKey` trong constructor:

```javascript
import { Transcript1 } from '@transcript1/sdk';

const t1 = new Transcript1(); // reads TRANSCRIPT1_API_KEY
const other = new Transcript1({ apiKey: 't1_...' });
```

Thiếu khóa, constructor sẽ throw `Transcript1Error` có mã `missing_api_key`.

## Gửi link hoặc file và chờ transcript

Gọi `transcribe(input, options)` để tạo job và chờ xử lý xong. Khi thành công, bạn nhận `CompletedTranscription` chứa `status: 'completed'` và `transcript`. Nếu job thất bại hoặc bị hủy, promise bị reject.

```javascript
const done = await t1.transcribe('https://youtu.be/dQw4w9WgXcQ');
console.log(done.transcript.text);

const file = await t1.transcribe('./interview.m4a', { speakers: true });
```

Nếu truyền Buffer hoặc Uint8Array, thêm tên file để SDK biết định dạng audio hoặc video:

```javascript
import { readFile } from 'node:fs/promises';

const buffer = await readFile('./call.mp3');
const call = await t1.transcribe(buffer, { filename: 'call.mp3' });
```

SDK hiểu chuỗi bắt đầu bằng `http://` hoặc `https://` là link, còn chuỗi khác là đường dẫn file. Bạn có thể gửi link YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn và Snapchat. Các nền tảng bổ sung yêu cầu HTTPS. Xem [Link video và tùy chọn](https://transcript1.com/vi/docs/sources-and-options) để kiểm tra dạng video công khai đã ghi hình được hỗ trợ, phụ đề và các giới hạn. File tải lên có dung lượng tối đa 2.000.000.000 byte, với đuôi mp3, wav, m4a, flac, ogg, opus, mp4, mov, webm hoặc mkv.

Chỉ gửi bài đăng công khai. Với bài có nhiều video, hệ thống xử lý video đầu tiên. Bạn không cần chỉ định nền tảng vì API tự nhận diện qua link. Các dạng link rút gọn dùng được nằm trong [tài liệu nguồn video](https://transcript1.com/vi/docs/sources-and-options).

SDK cũng nhận Blob, `ReadableStream<Uint8Array>` và async iterable trả từng chunk Uint8Array. Với Blob, cần truyền `filename`, trừ trường hợp File đã có tên. Với stream và async iterable, cần cả `filename` lẫn `size` tính bằng byte.

## Tải văn bản hoặc phụ đề

Dùng `download(id, options)` để chờ job hoàn tất rồi lấy nội dung dạng chuỗi. Kể cả khi chọn JSON, bạn vẫn nhận chuỗi JSON từ API chứ không phải object đã parse.

```javascript
const text = await t1.download(done.id); // text is the default
const withTimes = await t1.download(done.id, { timestamps: true });
const srt = await t1.download(done.id, { format: 'srt' });
const vtt = await t1.download(done.id, { format: 'vtt' });
const json = await t1.download(done.id, { format: 'json' });
```

Chỉ bật `timestamps: true` với định dạng `text`. Dùng với định dạng khác thì promise bị reject với `invalid_input`. Nếu job thất bại hoặc bị hủy, promise của `download` cũng bị reject.

## Gửi job rồi lấy transcript sau

Nếu không muốn chờ transcript, gọi `transcriptions.create`. Khi gửi file, hàm vẫn chờ tải lên xong rồi mới trả về:

```javascript
const job = await t1.transcriptions.create('./long-interview.mp4');
const now = await t1.transcriptions.get(job.id);
const finished = await t1.transcriptions.get(job.id, { wait: true });
```

Gọi `get` không kèm `wait` để đọc trạng thái hiện tại. Thêm `wait: true` để chờ đến `completed`, `failed` hoặc `canceled`. Luôn kiểm tra `finished.status` trước khi dùng transcript: khác với `transcribe`, job thất bại hoặc bị hủy không làm promise của `get` bị reject.

## Liệt kê job, xóa job và xem tài khoản

```javascript
for await (const item of t1.transcriptions.list({ status: 'completed', limit: 50 })) {
  console.log(item.id, item.created_at);
}

await t1.transcriptions.delete(job.id);
const account = await t1.account();
console.log(account.minutes.remaining);
```

Khi bạn duyệt `list`, SDK tự lấy từng trang. Có thể lọc theo `status` hoặc `platform`: `youtube`, `x`, `tiktok`, `instagram`, `facebook`, `bluesky`, `twitch`, `dailymotion`, `pinterest`, `linkedin`, `snapchat` hoặc `upload`. Đặt `limit` từ 1 đến 100 để chọn số mục mỗi trang; API mặc định là 20. Tên trường trong object vẫn giữ dạng `snake_case` của API. Giữ nguyên chuỗi `media_id`, không suy diễn cấu trúc của nó.

Với job còn chạy, `delete` sẽ hủy job. Với job đã kết thúc, lệnh này xóa cả job lẫn transcript. Gọi `account` để xem gói, số phút và hạn mức tài khoản.

## Cấu hình và tham số

Bạn có thể truyền `apiKey`, `baseUrl`, `fetch` và `maxRetries` vào constructor. Nếu không truyền `baseUrl`, SDK dùng `TRANSCRIPT1_API_URL` khi biến này có giá trị; nếu không, dùng `https://api.transcript1.com`. Truyền hàm `fetch` riêng nếu cần. `maxRetries` nhận số nguyên từ 0 đến 10, mặc định là 2.

Các tham số sau dùng được cho cả `transcribe` và `transcriptions.create`:

| Tham số | Cách dùng |
|---|---|
| `speakers` | Bật nhận diện người nói. |
| `language` | Chọn mã ngôn ngữ ISO 639-1, hoặc để mặc định `auto`. |
| `source` | Với link, chọn `auto` hoặc `stt`. YouTube, X, TikTok, Bluesky, Dailymotion và LinkedIn còn nhận `captions` nếu có track phù hợp. |
| `fresh` | Với link, tạo job mới thay vì dùng job cũ trong 24 giờ qua. |
| `filename` | Đặt tên file; bắt buộc khi truyền Buffer, Blob chưa có tên hoặc stream. Cũng dùng để ghi đè tên file có sẵn. |
| `size` | Khai báo dung lượng bằng byte; bắt buộc với stream và async iterable. |
| `idempotencyKey` | Nếu không truyền, SDK tạo UUID mới cho mỗi lần gọi. |
| `onProgress` | Callback nhận sự kiện về tiến độ. |
| `signal` | Truyền AbortSignal khi cần hủy lời gọi. |

Với `transcriptions.get`, bạn có `wait`, `includeWords`, `onProgress` và `signal`. Bật `includeWords: true` để lấy mốc thời gian từng từ. Callback `onProgress` báo thay đổi trong khi chờ.

Với `download`, dùng `format`, `timestamps` và `signal`. `list` cũng nhận `signal`. Để hủy lời gọi `transcriptions.delete(id, options)` hoặc `account(options)`, truyền `{ signal }` vào đối số options.

## Theo dõi tiến độ

Gắn callback `onProgress` vào `transcribe`, `transcriptions.create` hoặc `transcriptions.get` khi bật chế độ chờ:

```javascript
await t1.transcribe('./interview.m4a', {
  onProgress(event) {
    console.log(event);
  },
});
```

| Sự kiện | Dữ liệu nhận được |
|---|---|
| `upload` | Số byte đã gửi trong `sent` và tổng dung lượng trong `total`. |
| `status` | Trạng thái hiện tại của job trong `status`. |
| `waiting_slot` | `retryAfter` cho biết cần chờ bao nhiêu giây trước khi thử tìm chỗ xử lý job lần nữa. |
| `reconnecting` | Không kèm trường khác. Mỗi đợt mất kết nối trong khi chờ chỉ phát sự kiện này một lần. |

## Hủy lời gọi hoặc đặt timeout

Để dừng lời gọi, truyền `AbortSignal` từ AbortController. Nếu muốn giới hạn thời gian, dùng `AbortSignal.timeout`:

```javascript
await t1.transcribe('https://youtu.be/dQw4w9WgXcQ', {
  signal: AbortSignal.timeout(10 * 60_000),
});
```

Hủy trước request đầu tiên thì promise bị reject với `aborted`, chưa có ID job. Nếu hủy trong khi tải file lên, SDK thử xóa job khi nó còn ở `awaiting_upload`; sau đó promise bị reject với `aborted` kèm ID.

Hủy chờ không hủy job trên server. Job vẫn chạy, và bạn có thể lấy `transcriptionId` từ lỗi để chờ tiếp bằng `transcriptions.get(id, { wait: true })`. Muốn hủy cả job, gọi `transcriptions.delete(id)`.

Khi chờ job, SDK tự thử lại không giới hạn nếu mất mạng hoặc nhận HTTP 429, 500, 502, 503 hay 504. Khoảng chờ kết nối lại tăng dần: 2, 4, 8, 16 rồi 30 giây. Các request khác tuân theo `maxRetries`. Bước tạo job cũng chờ và thử lại nếu đã hết chỗ xử lý đồng thời. Dùng `signal` để giới hạn tổng thời gian chờ.

## Bắt và xử lý lỗi

Lỗi từ API, quá trình xử lý job và SDK đều có thể đọc qua `Transcript1Error`:

```javascript
import { Transcript1Error } from '@transcript1/sdk';

try {
  await t1.transcribe('./call.mp3');
} catch (error) {
  if (error instanceof Transcript1Error) {
    console.error(error.code, error.message, error.transcriptionId);
  }
}
```

| Trường | Thông tin lỗi |
|---|---|
| `message` | Nội dung mô tả sự cố. |
| `code` | Mã lỗi API, mã lỗi job, `canceled` hoặc mã lỗi riêng của SDK. |
| `status` | Mã HTTP. Lỗi job và lỗi phát sinh trong SDK không có trường này. |
| `details` | Thông tin API bổ sung như `minutes_needed` hay `action_url`. Mặc định là object rỗng. |
| `retryable` | Cho biết thử lại có thể thành công không; mặc định là `false`. |
| `transcriptionId` | ID job, nếu đã có. |
| `transcription` | Object transcription mới nhất SDK đã nhận, nếu có. |

Các lỗi riêng của SDK dùng mã `missing_api_key`, `invalid_input`, `aborted`, `network_error` và `upload_token_refused`. Nếu response lỗi không phải JSON, mã sẽ có dạng `http_<status>`.

Nếu job thất bại nhưng không kèm object lỗi, SDK dùng `transcription_failed`. Nếu đã lấy job hoàn tất về mà vẫn thiếu transcript, promise của `transcribe` bị reject với `internal_error`. Tra mã API trong [Xử lý lỗi](https://transcript1.com/vi/docs/errors) và các trường response trong [Tài liệu API](https://transcript1.com/vi/docs/api-reference).

## Môi trường chạy

SDK yêu cầu Node 20.19+. Bun và Deno dự kiến cũng chạy được nhờ lớp tương thích `node:`, nhưng bộ test tự động chỉ chạy trên Node. Chỉ dùng SDK trên server. Không đưa khóa API vào code gửi xuống trình duyệt.
