@clipto/live-recording
v0.1.0
Published
Browser PCM recording and Clipto realtime transcription engine
Readme
@clipto/live-recording
Clipto 浏览器实时录音核心。第一版面向 clipto-website-next 的稳定模块边界,
不是任意 ASR 服务的通用客户端;公开 npm 分发不代表获得 Clipto 后端访问权限。
范围
- 麦克风预热、AudioWorklet、PCM16 单声道采集、暂停恢复、停止尾帧。
- 本地分片、流式 MP3 导出、有序 Outbox、重连及待发送音频补发。
- Clipto 实时识别协议、转录片段合并、结束确认、录音互斥和技术事件。
- 不包含 UI、React、Next.js、账号登录、任务创建/完成/删除、S3 补传或业务权益。
主站的任务编排和失败补偿仍由调用方实现。保持原有单例模式,一次只录制一段音频。
历史类名 LiveReorder 保留兼容,根入口同时提供 LiveRecorder 别名。
安装和初始化
npm install --save-exact @clipto/[email protected]在创建转录引擎或首次存储 I/O 之前配置一次;录音运行期间禁止换端点或数据库。
import {
configureLiveRecording,
LiveTranscription,
} from "@clipto/live-recording";
configureLiveRecording({
// 必须传完整协议路径,不自动补 /mobile;这里不包含真实服务地址或凭据。
webSocketUrl: "wss://your-service.example/ws/speech/mobile",
telemetry: (action, properties) => {
// 可选:转交应用现有技术事件出口。不要在这里上传音频或转录内容。
},
});
const live = LiveTranscription.getInstance();
live.on("Response-partial", (message) => {
/* 更新临时转录 */
});
live.on("Response-final", (message) => {
/* 更新最终片段 */
});
live.on("connectionStateChange", (state) => {
/* 展示连接状态 */
});
live.on("Response-error", (error) => {
/* 接入应用错误处理 */
});
// 必须从用户交互启动,并由应用提供真实身份与唯一录音键。
const started = await live.start({
userID: "user-id-from-your-application",
recordingKey: "unique-recording-key",
language: "auto",
limitRecordDuration: 3600,
});
if (started) {
// 由应用创建任务后绑定;SDK 不会替你创建任务。
live.attachTask({ taskID: "created-task-id", assetID: "created-asset-id" });
}SDK import 不请求麦克风、不创建 AudioContext、不打开数据库、不连接服务; 实际录音仅在浏览器执行。需要 HTTPS 或 localhost、安全上下文、麦克风授权及 AudioWorklet。 processor 作为包内 JS 模块发布,按需转为 Blob URL 加载,不需要拷贝静态文件; 部署方必须验证其 CSP 允许该加载方式。未承诺后台/锁屏时浏览器不会挂起音频。
生命周期与可靠性契约
live.pause();
live.resume();
const result = await live.stop(); // 正常结束
// await live.stop(true); // 用户明确取消(会按既有语义清理数据)
switch (result.delivery) {
case "confirmed":
// 收到协议 end 确认;SDK 按既有服务契约清理本地 PCM。
break;
case "unknown":
// 未收到可靠的结束确认;保留本地 PCM,交给主站核验/补传。
break;
case "canceled":
break;
}
// 只在退出录音产品域/登出/应用级卸载时释放;普通 SPA 切页不要 dispose。
await live.dispose();- 先持久化 PCM,再入 Outbox;网络发送不阻塞采集处理队列。
- Outbox commit 仅表示浏览器
WebSocket.send入队,不是逐块服务端 ACK。 delivery: confirmed表示现有协议的 end 确认,不是逐块音频持久化证明;不承诺 exactly-once。start()返回成功表示采集启动,不等于 WebSocket 已连通;离线时仍可保留音频等待恢复。dispose()释放资源而不等价于取消或删除恢复数据。- 本包不拥有账号归属判定或后端补偿队列。当前协议约定服务端完成音频上传和任务完成调用后才回 end;SDK 据此清理本地 PCM。应用不能仅凭一条连接状态删除本地音频。
- 实时转录协议沿用 16 kHz PCM16 单声道;不要为实时转录自定义采样率。
存储与旧数据接入
默认使用独立 IndexedDB 数据库 clipto-live-recording / recordings。
主站迁移必须通过 storage 注入原 localforage 实例,保持既有库、key 前缀和序列号。
默认新库不会自动扫描其他应用数据库;不要直接用默认库替代主站历史存储。
import type { RecordingStorageAdapter } from "@clipto/live-recording/config";适配接口:ready、keys、getItem、getItemStrict、setItemStrict、removeItemStrict。
所有 strict 方法必须拒绝 I/O 失败,不能吞错后返回伪成功。
import { LiveRecorderStorage } from "@clipto/live-recording/storage";
const storage = LiveRecorderStorage.getInstance();
const recordings = await storage.listInterrupted();
// 流式 MP3 编码,消费一块即释放,避免整段长音频驻留内存。
await storage.encodeMp3Streaming(recordings[0].recordingKey, async (chunk) => {
// 将 chunk 交给应用上传器。
});MP3 编码器按需从声明的 npm 依赖加载。数据库遍历不做账号授权,应用必须先核对录音归属。
clear(recordingKey) 是破坏性操作,只能由明确取消或完成核验后的业务流程调用。
公开入口
| 入口 | 职责 |
| ---------------------------- | -------------------------------------- |
| 根入口 | 配置、录音器、存储、转录引擎及公共类型 |
| /config | 启动配置和存储适配接口 |
| /recorder | 兼容采集类及采集参数 |
| /storage | 分片存储和导出 |
| /transcription | Clipto 实时转录 |
| /types、/recording-types | 协议、状态、停止结果和分片类型 |
| /diagnostics | 技术事件与脱敏工具 |
Outbox、DSP、默认数据库接线、语言映射和内部计时器不作为公开入口。 ESM + TypeScript 声明;不提供 CJS require 入口。
开发与发布
npm ci
npm run check
# 首次运行浏览器测试需安装 Chromium,或使用已安装的 Chrome。
npx playwright install chromium
npm run test:browser
# PLAYWRIGHT_CHANNEL=chrome npm run test:browser
npm pack --dry-run
npm pack
# 由有 @clipto scope 发布权限的维护者执行;不会在 build/pack 时自动发布。
npm publish --access publictest:browser 在隔离消费项目安装真实 tarball,以假麦克风和本地 WebSocket 服务验证
ESM 拆包、类型解析、Worklet、跨标签页锁、IndexedDB、MP3、暂停/继续和停止确认。
可传入 tarball 路径验证指定产物;这不替代真实后端、Safari/iOS、CSP 和长时间录音回归。
prepack 清理构建目录、编译并检查实际发布清单;prepublishOnly 再运行类型检查和测试。
仅发布 dist、README、LICENSE、package.json,不发布测试、源码、环境文件、锁文件或内部文档。
依赖保留为 npm 外部依赖,不把第三方实现重新捆进本包。Node >= 20.19 用于开发/构建。
发布前由维护者核对 scope 权限、包名/版本可用性、公开代码审查及浏览器回归。
本次抽离没有代为发布,也没有预先授予开源许可:当前 metadata 为 UNLICENSED、保留权利;
若决定开源,需由权利人选择许可证并更新 LICENSE 和 package.json。
主站发布前消费同版本本地 tarball,发布后替换为精确 npm 版本并提交 lockfile。 禁止在主站复制实现、修改 node_modules 或使用 patch-package/overrides 绕过核心维护流程。
