npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

omnilink-client

v2.1.1

Published

Omnilink Video Conferencing SDK

Readme

OmniLink Client SDK

基于 WebRTC + mediasoup 的高清视频会议客户端 SDK,提供完整的音视频通话、屏幕共享、实时聊天、会议管理等功能。

特性

  • 高清音视频通话 — 基于 WebRTC 的低延迟实时通信
  • 屏幕共享 — 支持分享整个屏幕或单个窗口
  • 实时聊天 — 会议内文字消息实时收发
  • 会议管理 — 创建、加入、离开、关闭会议
  • 等候室 — 主持人审批机制,控制入会人员
  • 权限控制 — 静音、禁用视频、强制离开等主持人功能
  • 多端支持 — 自动识别 PC / 平板 / 手机设备类型
  • 自动重连 — WebSocket 断线后自动恢复连接

版本信息

当前版本:v2.1.1

| 版本 | 日期 | 说明 | |------|------|------| | v2.1.1 | 2026-06-11 | 修复音频约束:getAudioTrack() 显式启用 echoCancellationnoiseSuppressionautoGainControl,解决近距离设备开会时的啸叫问题 | | v2.1.0 | 2026-06-10 | 新增 socketUrlbasePath 配置,支持 HTTP API 与 WebSocket 使用不同基础路径,适配 /omnilink 子路径部署 | | v2.0.10 | 2026-04-27 | 新增 getUserByOriginalId()getUsersByOriginalIds() 接口,支持通过外部系统用户登录名查询用户基础信息(无需 Token) | | v2.0.9 | 2026-04-24 | 邀请功能支持附带文本信息:session.inviteUser()session.inviteUsers()session.generateInviteLink() 新增可选 message 参数;meeting:invited 事件新增 message 字段 | | v2.0.8 | 2026-04-23 | 创建会议支持 startAt(预约开始时间)和 duration(会议时长),会议到期自动结束 | | v2.0.7 | 2026-04-23 | API Key 登录参数调整为 originalId(用户登录名),更符合企业 SSO 实际使用场景 | | v2.0.6 | 2026-04-23 | 新增 API Key 登录支持:client.loginWithApiKey()client.user.exchangeApiKey(),支持第三方应用通过预登录令牌接入 | | v2.0.5 | 2026-04-23 | 修复 client.meeting.getMeeting() 接口,现在可在未加入会议时获取会议详情 | | v2.0.4 | 2026-04-23 | 新增 session.inviteUsers() 批量邀请接口,支持一次邀请多个用户 | | v2.0.3 | 2026-04-23 | 修复 meeting:invited 事件监听:被邀请方现在可通过 client.onMeetingInvited() 接收会议邀请推送 | | v2.0.2 | — | 初始稳定版本 |

查看完整 更新日志

安装

npm install omnilink-client

快速开始

1. 初始化客户端

import { OmniLinkClient } from 'omnilink-client'

const client = new OmniLinkClient({
  serviceUrl: 'https://your-server.com'
})

2. 登录

try {
  const result = await client.login({
    originalId: 'user001',      // 用户登录名
    password: '123456',         // 密码
    deviceType: 'pc',           // 设备类型:pc / mobile / tablet
    deviceId: 'device-001'      // 设备唯一标识
  })
  console.log('登录成功', result.userId)
} catch (err) {
  console.error('登录失败', err.message)
}

通过 API Key 登录(第三方接入 / 企业 SSO)

适用于企业已有统一身份认证系统、无需用户输入密码的场景。

架构说明apiKey 是敏感密钥,必须由第三方企业服务端保管和调用 exchange 接口;前端只接收 preLoginToken 并调用 login。严禁将 apiKey 暴露到前端代码或浏览器中。

Step 1 — 企业服务端调用 exchange(HTTP 原始接口):

第三方服务端可用任意语言(Java、Python、Go、PHP 等)直接调用 OmniLink HTTP 接口:

# cURL 示例
curl -X POST https://api.example.com/api/v1/apikey/exchange \
  -H "Content-Type: application/json" \
  -d '{
    "apiId": "ak_c18d803d05e20348",
    "apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
    "originalId": "zhangsan"
  }'
# Python 示例
import requests

res = requests.post("https://api.example.com/api/v1/apikey/exchange", json={
    "apiId": "ak_c18d803d05e20348",
    "apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
    "originalId": "zhangsan"
})
data = res.json()["data"]
pre_login_token = data["preLoginToken"]   # 5分钟有效
// Java (OkHttp) 示例
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(
    MediaType.parse("application/json"),
    "{\"apiId\":\"ak_c18d803d05e20348\",\"apiKey\":\"sk_...\",\"originalId\":\"zhangsan\"}"
);
Request request = new Request.Builder()
    .url("https://api.example.com/api/v1/apikey/exchange")
    .post(body)
    .build();
Response response = client.newCall(request).execute();
// 解析 response 获取 preLoginToken

Step 2 — 企业服务端将 preLoginToken 返回给前端:

{
  "preLoginToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 300,
  "userId": "69cf72ac6359168b78c504d3",
  "userName": "张三"
}

Step 3 — 前端用 preLoginToken 完成登录:

const loginResult = await client.loginWithApiKey({
  preLoginToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', // 来自服务端
  deviceType: 'web',
  deviceId: 'enterprise-portal-browser-001'
})

console.log('API Key 登录成功', loginResult.userId)

3. 创建并加入会议

// 创建会议(自动加入)
const session = await client.createAndJoinMeeting({
  meetingName: '产品周会',
  waitingRoom: false,           // 是否开启等候室
  startAt: Date.now(),          // 会议开始时间(时间戳,默认当前时间)
  duration: 60,                 // 会议时长(分钟,默认无限制)
})

console.log('会议ID:', session.meetingId)
console.log('会议密码:', session.password)

4. 初始化媒体并推流

// 初始化 mediasoup 媒体客户端
await session.initMediaClient()

// 获取本地音视频
const stream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
})

// 发布视频
const videoTrack = stream.getVideoTracks()[0]
const { producerId } = await session.produceTrack(videoTrack, { kind: 'video' })

// 发布音频
const audioTrack = stream.getAudioTracks()[0]
await session.produceTrack(audioTrack, { kind: 'audio' })

5. 拉取远端流

// 订阅其他成员的音视频
client.onProducerCreated(async ({ producerId, kind, userId }) => {
  // 拉流
  const { stream, consumerId } = await session.consume(producerId, kind)

  // 将流绑定到 video/audio 元素
  const videoEl = document.getElementById(`video-${userId}`) as HTMLVideoElement
  videoEl.srcObject = stream
  videoEl.play()
})

6. 实时聊天

// 发送消息
await session.sendChatMessage('大家好,会议开始!')

// 接收消息
client.onChatMessage((msg) => {
  console.log(`${msg.senderName}: ${msg.content}`)
})

7. 监听会议事件

// 成员加入
client.onMemberJoined((data) => {
  console.log(`${data.userName} 加入了会议`)
})

// 成员离开
client.onMemberLeft((data) => {
  console.log(`${data.userId} 离开了会议`)
})

// 会议被关闭
client.onMeetingClosed((data) => {
  console.log('会议已关闭:', data.reason)
})

// 被踢出会议
client.onKicked((data) => {
  console.log('您已被踢出会议:', data.reason)
})

// 连接状态变化
client.onConnectionStateChange((state) => {
  console.log('连接状态:', state) // disconnected / connecting / connected / reconnecting
})

8. 离开会议

await client.leaveMeeting()

API 参考

OmniLinkClient

核心客户端类,继承自 EventEmitter。提供统一的认证、连接、会议入口。

import { OmniLinkClient } from 'omnilink-client'
// 或默认导出
import OmnilinkClient from 'omnilink-client'

构造函数

new OmniLinkClient(config?: OmniLinkClientConfig)

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | config | OmniLinkClientConfig | 否 | {} | 客户端配置对象 | | config.serviceUrl | string | 否 | 'http://localhost:3000' | 统一服务地址,推荐直接使用此字段配置 | | config.userServiceUrl | string | 否 | 继承 serviceUrl | 用户服务独立地址(旧版兼容) | | config.meetingServiceUrl | string | 否 | 继承 serviceUrl | 会议服务独立地址(旧版兼容) | | config.socketUrl | string | 否 | 继承 serviceUrl | WebSocket 连接地址。当 HTTP API 和 WebSocket 使用不同基础路径时设置,例如 API 通过 /omnilink/api 代理,但 socket.io 通过 /socket.io 代理 | | config.basePath | string | 否 | '' | 前端部署的子路径(如 /web),用于生成正确的邀请链接等完整 URL |

属性

| 属性 | 类型 | 说明 | |------|------|------| | user | UserService | 用户/认证服务实例,暴露登录、注册、资料管理等 HTTP API | | meeting | MeetingService | 会议服务实例,暴露会议创建、查询、加入等 HTTP API | | socket | SocketManager | WebSocket 管理实例,用于 Socket.IO 连接和事件收发 |

登录相关


login(credentials)

用户登录,成功后自动保存 token 到 localStorage,并自动建立 WebSocket 连接。

async login(credentials: LoginCredentials): Promise<LoginResult>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | credentials | LoginCredentials | 是 | 登录凭证 | | credentials.originalId | string | 是 | 用户登录名/账号 | | credentials.password | string | 是 | 用户密码 | | credentials.deviceType | string | 是 | 设备类型:'pc' / 'mobile' / 'tablet' | | credentials.deviceId | string | 是 | 设备唯一标识,用于多设备登录管理 | | credentials.userAgent | string | 否 | 浏览器 User-Agent(可选) | | credentials.force | boolean | 否 | 是否强制登录,用于挤掉其他设备的已有会话 |

返回值

Promise<LoginResult> — 登录成功结果:

  • token: string — JWT 认证令牌
  • userId: string — 用户唯一 ID
  • endpointId: string — 当前设备端点 ID
  • session: string — 会话标识
  • endpoints: Array<{ id, deviceId, deviceType, loginTime, session }> — 该账号所有在线设备列表

异常

  • 账号密码错误、账号不存在时抛出 HttpError
  • 账号已在其他设备登录时会返回 needForce: true,再次调用并传入 force: true 可强制登录

loginWithApiKey(credentials)

使用 API Key 预登录令牌登录,适用于第三方应用接入场景。

先通过 client.user.exchangeApiKey() 获取 preLoginToken,再调用此方法完成登录。 登录成功后自动保存 token 到 localStorage,并自动建立 WebSocket 连接。

async loginWithApiKey(credentials: ApiKeyLoginCredentials): Promise<LoginResult>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | credentials | ApiKeyLoginCredentials | 是 | API Key 登录凭证 | | credentials.preLoginToken | string | 是 | 预登录令牌(由 exchangeApiKey() 获取) | | credentials.deviceType | string | 是 | 设备类型:'pc' / 'mobile' / 'tablet' / 'web' | | credentials.deviceId | string | 是 | 设备唯一标识 | | credentials.userAgent | string | 否 | 用户代理字符串 | | credentials.force | boolean | 否 | 是否强制登录,挤掉同类型设备的已有会话 |

返回值

Promise<LoginResult> — 同 login() 的返回值

异常

  • 预登录令牌无效或过期时抛出 HttpError(401)
  • 账号已在其他设备登录时返回 needForce: true(409)

logout()

退出登录,断开 WebSocket 连接,清除本地 token。

async logout(): Promise<void>

返回值 Promise<void>


isLoggedIn()

检查当前是否已登录。

isLoggedIn(): boolean

返回值 booleantrue 表示已登录且 token 有效


restoreSession()

从 localStorage 恢复 token 并重新连接 WebSocket。适用于页面刷新后的自动重连场景。

async restoreSession(): Promise<boolean>

返回值 Promise<boolean>true 表示恢复成功并连接 WebSocket


exchangeApiKey(credentials)

使用 API Key 凭证交换预登录令牌。这是 API Key 登录的第一步。

async exchangeApiKey(credentials: ApiKeyExchangeCredentials): Promise<ApiKeyExchangeResult>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | credentials | ApiKeyExchangeCredentials | 是 | API Key 交换凭证 | | credentials.apiId | string | 是 | API 公开标识符,如 ak_xxxxxxxx | | credentials.apiKey | string | 是 | API 私密密钥,如 sk_xxxxxxxx | | credentials.userId | string | 是 | 目标用户 ID |

返回值

Promise<ApiKeyExchangeResult> — 交换结果:

  • preLoginToken: string — 预登录令牌(JWT,5 分钟有效)
  • expiresIn: number — 过期时间(秒,默认 300)
  • userId: string — 目标用户 ID
  • userName: string — 目标用户名称

异常

  • API ID 不存在、API Key 不正确(401)
  • API Key 已停用或过期(401)
  • 目标用户不存在(404)或被封禁/禁用(403)

loginWithApiKey(credentials)

使用预登录令牌完成登录(API Key 登录方式)。这是 API Key 登录的第二步。 内部会保存 token 到 localStorage。

async loginWithApiKey(credentials: ApiKeyLoginCredentials): Promise<LoginResponse>

参数说明OmniLinkClient.loginWithApiKey()credentials

返回值 Promise<LoginResponse> — 登录响应:

  • 成功:{ success: true, data: LoginResult }
  • 失败:{ success: false, code: number, message: string, data?: any, needForce?: boolean }

getProfile()

获取当前登录用户的详细信息。

async getProfile(): Promise<User>

返回值 Promise<User> — 用户信息:

  • id: string — 用户唯一 ID
  • originalId: string — 用户登录名
  • name: string — 用户昵称
  • avatar?: string — 头像 URL
  • email?: string — 邮箱
  • createdAt: Date — 注册时间

getCurrentUserId()

从当前 token 中解析并返回用户 ID。

getCurrentUserId(): string | null

返回值 string | null — 用户 ID,未登录时返回 null

连接相关


connectSocket()

手动连接 WebSocket。通常由 login() 自动调用,无需手动使用。

async connectSocket(): Promise<void>

异常 未登录(无 token)时抛出错误

返回值 Promise<void>


disconnectSocket(intentional?)

断开 WebSocket 连接。

disconnectSocket(intentional?: boolean): void

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | intentional | boolean | 否 | true | 是否为主动断开。主动断开会取消自动重连;非主动断开会触发自动重连 |


isConnected()

检查 WebSocket 是否已连接。

isConnected(): boolean

返回值 boolean


getConnectionState()

获取当前 WebSocket 连接状态。

getConnectionState(): ConnectionState

返回值 ConnectionState 枚举值:

  • 'disconnected' — 已断开
  • 'connecting' — 连接中
  • 'connected' — 已连接
  • 'reconnecting' — 自动重连中

会议相关


createAndJoinMeeting(data)

创建一个新会议并自动加入。等效于先调用 meeting.createMeeting() 再调用 meeting.joinMeeting(),最后通过 WebSocket 加入会议房间。

async createAndJoinMeeting(data: CreateMeetingData): Promise<MeetingSession>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | data | CreateMeetingData | 是 | — | 会议创建参数 | | data.meetingName | string | 否 | — | 会议名称 | | data.visitorAllowed | boolean | 否 | — | 是否允许访客入会 | | data.memberInviteAllowed | boolean | 否 | — | 是否允许成员邀请他人 | | data.multiEndpoint | boolean | 否 | — | 是否允许同一用户多设备入会 | | data.needPassword | boolean | 否 | — | 是否需要密码 | | data.allowScreenShare | boolean | 否 | — | 是否允许屏幕共享 | | data.allowRecording | boolean | 否 | — | 是否允许录制 | | data.waitingRoom | boolean | 否 | false | 是否开启等候室 | | data.muteOnEntry | boolean | 否 | — | 成员入会时是否自动静音 | | data.startAt | number | 否 | Date.now() | 会议开始时间(Unix 时间戳,毫秒) | | data.duration | number | 否 | — | 会议计划时长(分钟),设置后会议将在到期后自动结束 | | data.notice | string | 否 | — | 会议公告 | | data.inviteMembers | string[] | 否 | — | 初始邀请用户 ID 列表 |

返回值 Promise<MeetingSession> — 会议会话实例


joinMeeting(data)

通过会议 ID 和密码加入会议。

async joinMeeting(data: JoinMeetingData): Promise<MeetingSession>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | data | JoinMeetingData | 是 | 加入会议参数 | | data.meetingId | string | 是 | 会议 ID(9位数字) | | data.password | string | 是 | 会议密码(6位数字) | | data.deviceType | string | 否 | 设备类型:'pc' / 'mobile' / 'tablet' | | data.force | boolean | 否 | 是否强制加入(挤掉同设备已有会话) |

返回值 Promise<MeetingSession> — 会议会话实例


restoreMeeting(meetingId, password)

恢复已加入的会议。适用于页面刷新后重建会话但无需重新输入会议密码的场景。

async restoreMeeting(meetingId: string, password: string): Promise<MeetingSession | null>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | meetingId | string | 是 | 会议 ID | | password | string | 是 | 会议密码 |

返回值 Promise<MeetingSession | null> — 恢复成功返回会话实例,失败返回 null


leaveMeeting()

离开当前会议。调用后会议会话将不可用。

async leaveMeeting(): Promise<void>

返回值 Promise<void>


isInMeeting()

检查当前是否在会议中。

isInMeeting(): boolean

返回值 boolean


getCurrentSession()

获取当前会议会话实例。

getCurrentSession(): MeetingSession | null

返回值 MeetingSession | null — 当前会话,未在会议中时返回 null

事件监听

所有事件监听方法都返回 void,通过传入回调函数来接收事件。使用 offEvent(event, callback) 可取消监听。


onConnectionStateChange(callback)

WebSocket 连接状态变化时触发。

onConnectionStateChange(callback: (state: ConnectionState) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | state | ConnectionState | 新状态:'disconnected' / 'connecting' / 'connected' / 'reconnecting' |


onMemberJoined(callback)

有成员加入会议时触发。

onMemberJoined(callback: (data: MemberJoinedEvent) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | data.userId | string | 加入成员的用户 ID | | data.userName | string | 加入成员的昵称 | | data.role | 'host' \| 'member' | 角色 | | data.avatar? | string | 头像 URL | | data.online? | boolean | 是否在线 |


onMemberLeft(callback)

有成员离开会议时触发。

onMemberLeft(callback: (data: MemberLeftEvent) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | data.userId | string | 离开成员的用户 ID |


onMembersUpdated(callback)

会议成员列表发生变化时触发(如有人加入或离开)。

onMembersUpdated(callback: (data: MembersUpdatedEvent) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | data.members | MemberInfo[] | 当前所有在线成员列表,每个成员包含 userIduserNameroleavataronline |


onProducerCreated(callback)

有成员开始推流(发布音视频)时触发。应在此回调中调用 session.consume() 拉取远端流。

onProducerCreated(callback: (data: ProducerEvent) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | data.producerId | string | 生产者 ID | | data.kind | 'audio' \| 'video' | 媒体类型。注意:屏幕共享的 kind 也是 'video',可通过 appData 区分 | | data.userId? | string | 推流用户的 ID | | data.appData? | any | 应用自定义数据,如 { kind: 'screen' } |


onExistingProducers(callback)

加入会议后,获取会议中已有的推流列表时触发。

onExistingProducers(callback: (data: { producers: ProducerInfo[] }) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | data.producers | ProducerInfo[] | 已有生产者列表,每项包含 idkindappData |


onProducerClosed(callback)

有成员停止推流时触发。

onProducerClosed(callback: (data: ProducerEvent) => void): void

回调参数onProducerCreated


onChatMessage(callback)

收到聊天消息时触发。

onChatMessage(callback: (data: { senderId: string; senderName: string; content: string; time: Date }) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | senderId | string | 发送者用户 ID | | senderName | string | 发送者昵称 | | content | string | 消息内容 | | time | Date | 发送时间 |


onMeetingClosed(callback)

会议被主持人关闭时触发。

onMeetingClosed(callback: (data: { meetingId: string; reason?: string }) => void): void

onKicked(callback)

被踢出会议时触发。

onKicked(callback: (data: { meetingId: string; reason?: string }) => void): void

onNoticePublished(callback)

收到主持人发布的公告时触发。

onNoticePublished(callback: (data: { content: string; senderId: string }) => void): void

onPermissionsChanged(callback)

自己的会议权限发生变化时触发(如被主持人静音/取消静音)。

onPermissionsChanged(callback: (data: PermissionsChangedEvent) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | data.meetingId | string | 会议 ID | | data.permissions | object | 权限对象,包含 video?audio?screen?text? 布尔值 |


onSocketConnected(callback)

WebSocket 连接成功时触发。

onSocketConnected(callback: () => void): void

onSocketDisconnected(callback)

WebSocket 断开时触发。

onSocketDisconnected(callback: () => void): void

onReconnecting(callback)

正在自动重连时触发。

onReconnecting(callback: (data: { attempt: number; maxAttempts: number; delay: number }) => void): void

回调参数

| 参数 | 类型 | 说明 | |------|------|------| | attempt | number | 当前重连次数 | | maxAttempts | number | 最大重连次数 | | delay | number | 距离下次重连的延迟(毫秒) |


onReconnected(callback)

WebSocket 重连成功时触发。

onReconnected(callback: () => void): void

onReconnectFailed(callback)

WebSocket 自动重连失败(达到最大重试次数)时触发。

onReconnectFailed(callback: () => void): void

onKickedFromServer(callback)

被服务端踢下线时触发(如账号在其他设备登录)。

onKickedFromServer(callback: (data: { deviceId: string; session: string; reason: string }) => void): void

onMeetingInvited(callback)

收到会议邀请时触发。当主持人调用 session.inviteUser(userId)session.inviteUsers(userIds) 邀请当前用户时,被邀请方会收到此事件。如果邀请时附带了 message,会一同传递。

onMeetingInvited(callback: (data: MeetingInvitedEvent) => void): void

回调参数

| 字段 | 类型 | 说明 | |------|------|------| | meetingId | string | 会议 ID | | invitedBy | string | 邀请人用户 ID | | message | string | 邀请附带的文本信息(如邀请语),邀请时未传则为 undefined | | timestamp | number | 邀请时间戳 |

使用示例

client.onMeetingInvited((data) => {
  console.log(`收到会议邀请,会议ID: ${data.meetingId},邀请人: ${data.invitedBy}`)
  if (data.message) {
    console.log(`邀请语: ${data.message}`)
  }
  // 弹出邀请提示框,或自动跳转加入会议
})

offEvent(event, callback)

取消指定事件的监听。

offEvent<K extends keyof SocketEventMap>(event: K, callback: (data: SocketEventMap[K]) => void): void

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | event | EventName | 是 | 事件名称 | | callback | function | 是 | 注册时使用的回调函数 |


ConnectionState

WebSocket 连接状态枚举。

enum ConnectionState {
  DISCONNECTED = 'disconnected',
  CONNECTING = 'connecting',
  CONNECTED = 'connected',
  RECONNECTING = 'reconnecting',
}

UserService

用户认证 HTTP 服务,通过 client.user 访问。所有 token 自动持久化到 localStorage。


setToken(token)

手动设置用户认证 token。

setToken(token: string): void

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | token | string | 是 | JWT 认证令牌 |


getToken()

获取当前用户 token。

getToken(): string | null

返回值 string | null


clearToken()

清除本地保存的用户 token。

clearToken(): void

register(data)

用户注册。

async register(data: RegisterData): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | data | RegisterData | 是 | 注册信息 | | data.originalId | string | 是 | 用户登录名/账号 | | data.username | string | 是 | 用户昵称 | | data.password | string | 是 | 密码 | | data.email | string | 否 | 邮箱 |


login(credentials)

用户登录。内部会保存 token 到 localStorage。

async login(credentials: LoginCredentials): Promise<LoginResponse>

参数说明OmniLinkClient.login()credentials

返回值 Promise<LoginResponse> — 登录响应:

  • 成功:{ success: true, data: LoginResult }
  • 失败:{ success: false, code: number, message: string, data?: any, needForce?: boolean }

logout()

退出登录,调用后端注销接口并清除本地 token。

async logout(): Promise<void>

getProfile()

获取当前用户资料。

async getProfile(): Promise<User>

返回值 Promise<User> — 用户信息,字段见 OmniLinkClient.getProfile()


getUsers()

获取系统中的用户列表。

async getUsers(): Promise<User[]>

返回值 Promise<User[]> — 用户数组


updateName(name)

修改当前用户昵称。

async updateName(name: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | name | string | 是 | 新昵称 |


updateAvatar(avatar)

修改当前用户头像。

async updateAvatar(avatar: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | avatar | string | 是 | 头像图片 URL |


updatePassword(oldPassword, newPassword)

修改当前用户密码。

async updatePassword(oldPassword: string, newPassword: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | oldPassword | string | 是 | 旧密码 | | newPassword | string | 是 | 新密码 |


isLoggedIn()

检查是否已登录。

isLoggedIn(): boolean

返回值 boolean


getSessions()

获取当前账号的所有登录会话列表。

async getSessions(): Promise<any[]>

返回值 Promise<any[]> — 会话列表,每项包含设备信息、登录时间等


getUserByOriginalId(originalId)

通过外部系统用户登录名(originalId)获取用户基础信息。无需登录即可调用,属于公开接口。

async getUserByOriginalId(originalId: string): Promise<UserBasicInfo>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | originalId | string | 是 | 用户登录名/账号(外部系统标识) |

返回值 Promise<UserBasicInfo> — 用户基础信息:

  • userId: string — OmniLink 内部用户 ID
  • originalId: string — 用户登录名
  • name: string — 用户昵称
  • avatar?: string — 头像 URL

异常

  • 用户不存在时抛出 HttpError(404)

使用示例

try {
  const user = await client.user.getUserByOriginalId('zhangsan')
  console.log('用户ID:', user.userId)
  console.log('昵称:', user.name)
} catch (err) {
  console.error('用户不存在')
}

getUsersByOriginalIds(originalIds)

批量通过外部系统用户登录名(originalId)获取多个用户的基础信息。无需登录即可调用,属于公开接口。

async getUsersByOriginalIds(originalIds: string[]): Promise<UserBasicInfo[]>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | originalIds | string[] | 是 | 用户登录名数组(非空) |

返回值 Promise<UserBasicInfo[]> — 匹配到的用户基础信息数组。未匹配到的 originalId 将被自动忽略,不会报错。

使用示例

const users = await client.user.getUsersByOriginalIds(['zhangsan', 'lisi', 'wangwu'])
console.log(`查询 ${users.length} 个用户`)
users.forEach(u => {
  console.log(u.originalId, '->', u.userId, u.name)
})

MeetingService

会议 HTTP 服务,通过 client.meeting 访问。负责会议的发现、创建、加入和邀请验证。


createMeeting(data)

创建一个新会议。

async createMeeting(data: CreateMeetingData): Promise<{ meetingId: string; password: string }>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | data | CreateMeetingData | 是 | — | 同 OmniLinkClient.createAndJoinMeeting() 的参数 |

返回值 Promise<{ meetingId: string; password: string }> — 新创建会议的 ID 和密码


listMeetings(params?)

分页获取会议列表。

async listMeetings(params?: PaginationParams): Promise<MeetingListResult>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | params | PaginationParams | 否 | — | 分页参数 | | params.page | number | 否 | — | 页码,从 1 开始 | | params.pageSize | number | 否 | — | 每页条数 |

返回值 Promise<MeetingListResult> — 分页结果:

  • meetings: Meeting[] — 会议列表
  • total: number — 总条数
  • page: number — 当前页码
  • pageSize: number — 每页条数
  • totalPages: number — 总页数

getMeeting(meetingId)

获取会议详情。

async getMeeting(meetingId: string): Promise<Meeting>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | meetingId | string | 是 | 会议 ID |

返回值 Promise<Meeting> — 会议详情:

  • id: string — 会议 ID
  • meetingName: string — 会议名称
  • creator: string — 创建者用户 ID
  • password?: string — 密码
  • visitorAllowed: boolean — 是否允许访客
  • memberInviteAllowed?: boolean — 是否允许成员邀请
  • multiEndpoint?: boolean — 是否允许多端
  • needPassword?: boolean — 是否需要密码
  • allowScreenShare?: boolean — 是否允许屏幕共享
  • allowRecording?: boolean — 是否允许录制
  • waitingRoom?: boolean — 是否开启等候室
  • muteOnEntry?: boolean — 入会是否自动静音
  • notice?: string — 公告
  • status: 'active' | 'closed' — 会议状态
  • createdAt: Date — 创建时间
  • updatedAt: Date — 更新时间
  • forbidden?: object — 全局禁言状态 { video, audio, screen, text, consumer }

getMeetingMembers(meetingId)

获取会议的成员列表。

async getMeetingMembers(meetingId: string): Promise<MeetingMember[]>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | meetingId | string | 是 | 会议 ID |

返回值 Promise<MeetingMember[]> — 成员数组,每项包含:

  • userId: string — 用户 ID
  • userName: string — 昵称
  • avatar?: string — 头像
  • role: 'host' | 'member' — 角色
  • joinedAt: Date — 入会时间
  • online?: boolean — 是否在线
  • endpoints?: Array<...> — 端点信息
  • hasAudio?: boolean — 是否有音频
  • forbidden?: object — 被禁止的权限

joinMeeting(data)

加入会议,返回 MeetingSession 会话实例。

async joinMeeting(data: JoinMeetingData): Promise<MeetingSession>

参数说明OmniLinkClient.joinMeeting(data)

返回值 Promise<MeetingSession> — 会议会话实例


restoreSession(meetingId?, password?)

从 localStorage 中存储的 meeting token 恢复会议会话。

async restoreSession(meetingId?: string, password?: string): Promise<MeetingSession | null>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | meetingId | string | 否 | 会议 ID,不传则从 localStorage 读取 | | password | string | 否 | 会议密码,不传则从 localStorage 读取 |

返回值 Promise<MeetingSession | null> — 恢复成功返回会话,失败返回 null


hasMeetingToken()

检查本地是否有会议 token。

hasMeetingToken(): boolean

返回值 boolean


getMeetingToken()

获取本地保存的会议 token。

getMeetingToken(): string | null

返回值 string | null


clearMeetingToken()

清除本地保存的会议 token 和会话信息。

clearMeetingToken(): void

getCurrentSession()

获取当前的会议会话实例。

getCurrentSession(): MeetingSession | null

返回值 MeetingSession | null


verifyInviteLink(token)

验证邀请链接是否有效。

async verifyInviteLink(token: string): Promise<{ valid: boolean; meetingId?: string; message?: string }>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | token | string | 是 | 邀请链接中的 token |

返回值 Promise<{ valid: boolean; meetingId?: string; message?: string }>


joinByInviteLink(token, deviceType?, force?)

通过邀请链接加入会议。

async joinByInviteLink(token: string, deviceType?: string, force?: boolean): Promise<MeetingSession>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | token | string | 是 | — | 邀请 token | | deviceType | string | 否 | — | 设备类型 | | force | boolean | 否 | false | 是否强制加入 |

返回值 Promise<MeetingSession> — 会议会话实例


MeetingSession

会议会话实例,通过 client.createAndJoinMeeting()client.joinMeeting()client.meeting.joinMeeting() 获取。管理会议内的所有操作,包括媒体推流/拉流、聊天、成员管理、权限控制等。

注意:会话实例通常由 SDK 内部创建,不应直接实例化。

属性

| 属性 | 类型 | 说明 | |------|------|------| | meetingId | string | 会议 ID(9位数字) | | password | string | 会议密码(6位数字) | | meetingToken | string | 会议令牌(内部使用,每次请求自动附加到 header) | | closed | boolean | 会话是否已关闭。关闭后不可再使用 | | isMediaClientInitialized | boolean | 媒体客户端是否已初始化,只有为 true 时才能调用推流/拉流方法 |

媒体生命周期


initMediaClient()

初始化媒体客户端。必须在开始推流或拉流前调用。

内部流程:

  1. 创建 MediaClientNode 实例
  2. 向后端请求服务器 RTP 能力
  3. 使用 RTP 能力初始化本地 mediasoup Device
async initMediaClient(): Promise<void>

异常

  • 会话已关闭时抛出 Error
  • 后端返回失败或设备初始化失败时抛出

返回值 Promise<void>


produceTrack(track, appData, encodings?)

发布本地媒体轨道到会议。

async produceTrack(
  track: MediaStreamTrack,
  appData: { kind: 'audio' | 'video' | 'screen' },
  encodings?: types.RtpEncodingParameters[]
): Promise<ProduceResult>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | track | MediaStreamTrack | 是 | 媒体轨道,通过 getUserMedia()getDisplayMedia() 获取 | | appData | object | 是 | 应用数据 | | appData.kind | 'audio' \| 'video' \| 'screen' | 是 | 媒体类型标识。screen 在 mediasoup 内部也会被映射为 video | | encodings | RtpEncodingParameters[] | 否 | RTP 编码参数,用于设置码率、分辨率层级等高级参数 |

返回值 Promise<ProduceResult> — 推流结果:

  • producerId: string — 生产者 ID,用于后续管理该推流(暂停/恢复/关闭)

异常

  • 媒体客户端未初始化时抛出
  • 轨道状态非 'live' 时抛出
  • 创建生产者失败时抛出

produceWithDevice(deviceId, kind)

通过设备 ID 便捷推流。自动获取指定设备的媒体轨道并开始推流。

async produceWithDevice(deviceId: string, kind: 'audio' | 'video'): Promise<ProduceResult>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | deviceId | string | 是 | 设备 ID,通过 listVideoInputDevices()listAudioInputDevices() 获取 | | kind | 'audio' \| 'video' | 是 | 媒体类型 |

返回值 Promise<ProduceResult> — 推流结果


closeProducer(producerId)

关闭指定生产者,停止向会议发送媒体流。

async closeProducer(producerId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | producerId | string | 是 | 要关闭的生产者 ID |


pauseProducer(producerId)

暂停指定生产者。暂停后媒体流不再发送到会议,但轨道保持打开,可通过 resumeProducer() 恢复。

async pauseProducer(producerId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | producerId | string | 是 | 生产者 ID |


resumeProducer(producerId)

恢复被暂停的生产者,重新开始发送媒体流。

async resumeProducer(producerId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | producerId | string | 是 | 生产者 ID |


getProducerState(producerId)

获取指定生产者的当前状态。

getProducerState(producerId: string): { paused: boolean; closed: boolean } | undefined

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | producerId | string | 是 | 生产者 ID |

返回值 { paused: boolean; closed: boolean } | undefined — 生产者状态,未找到时返回 undefined


consume(producerId, kind)

订阅远端媒体流。

async consume(producerId: string, kind: 'audio' | 'video'): Promise<ConsumeResult>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | producerId | string | 是 | 远端生产者 ID(从 onProducerCreated 事件中获得) | | kind | 'audio' \| 'video' | 是 | 媒体类型 |

返回值 Promise<ConsumeResult> — 消费结果:

  • consumerId: string — 消费者 ID
  • stream: MediaStream — 媒体流对象,可直接绑定到 <video><audio> 元素的 srcObject
  • kind: 'audio' | 'video' — 媒体类型
  • producerId: string — 对应的生产者 ID

closeConsumer(consumerId)

关闭指定消费者,停止接收远端媒体流。

async closeConsumer(consumerId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | consumerId | string | 是 | 消费者 ID |


pauseConsumer(consumerId)

暂停接收媒体流,但保持 Consumer 连接。

async pauseConsumer(consumerId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | consumerId | string | 是 | 消费者 ID |


resumeConsumer(consumerId)

恢复接收媒体流。

async resumeConsumer(consumerId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | consumerId | string | 是 | 消费者 ID |


getStats()

获取底层 WebRTC 统计信息。

async getStats(): Promise<{ transportId: string; direction: string; stats: RTCStatsReport }[]>

返回值 Promise<Array<{ transportId, direction, stats }>> — 所有传输的统计报告数组

会议信息


meetingInfo()

获取当前会议的详细信息。

async meetingInfo(): Promise<Meeting>

返回值 Promise<Meeting> — 会议详情,字段见 MeetingService.getMeeting()


getMembers()

获取当前会议内的在线成员列表。

async getMembers(): Promise<Member[]>

返回值 Promise<Member[]> — 成员数组,包含 userIduserNameroleavataronline 等字段

聊天


sendChatMessage(content, type?, fileMeta?)

发送聊天消息到会议。

async sendChatMessage(
  content: string,
  type?: string,
  fileMeta?: { fileUrl?: string; fileName?: string; fileSize?: number }
): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | content | string | 是 | — | 消息内容 | | type | string | 否 | 'text' | 消息类型:textimagevideofilesystemnotice | | fileMeta | object | 否 | — | 文件元数据(发送文件/图片消息时使用) | | fileMeta.fileUrl | string | 否 | — | 文件 URL | | fileMeta.fileName | string | 否 | — | 文件名 | | fileMeta.fileSize | number | 否 | — | 文件大小(字节) |


uploadMeetingFile(file)

上传会议文件(图片、文档等)。

async uploadMeetingFile(file: File): Promise<{ fileUrl: string; fileName: string; fileSize: number }>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | file | File | 是 | 浏览器 File 对象 |

返回值 Promise<{ fileUrl, fileName, fileSize }> — 上传结果


getChatHistory(limit?)

获取会议内的历史聊天消息。

async getChatHistory(limit?: number): Promise<ChatMessage[]>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | limit | number | 否 | 50 | 返回消息数量限制 |

返回值 Promise<ChatMessage[]> — 消息数组,每项包含:

  • id?: string — 消息 ID
  • senderId: string — 发送者 ID
  • senderName: string — 发送者昵称
  • avatar?: string — 发送者头像
  • content: string — 内容
  • time: Date | string — 时间
  • type?: 'text' | 'image' | 'video' | 'file' | 'system' | 'notice' — 类型
  • fileUrl?fileName?fileSize? — 文件信息

主持人控制

以下方法需要主持人权限,后端会进行权限校验。


forbidMemberSpeak(memberId, kind?)

禁止指定成员发言或发送视频。

async forbidMemberSpeak(
  memberId: string,
  kind?: 'audio' | 'video' | 'screen' | 'text'
): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | memberId | string | 是 | — | 目标成员用户 ID | | kind | string | 否 | 'audio' | 禁止类型:audio(静音)、video(关闭视频)、screen(禁止共享)、text(禁止聊天) |


permitMemberSpeak(memberId, kind?)

允许指定成员恢复发言或视频。

async permitMemberSpeak(
  memberId: string,
  kind?: 'audio' | 'video' | 'screen' | 'text'
): Promise<void>

参数说明forbidMemberSpeak


forbidAllSpeak(kind?)

全员静音(禁止所有非主持人成员发言)。

async forbidAllSpeak(kind?: 'audio' | 'video' | 'screen' | 'text'): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | kind | string | 否 | 'audio' | 禁止类型 |


permitAllSpeak(kind?)

允许全员恢复发言。

async permitAllSpeak(kind?: 'audio' | 'video' | 'screen' | 'text'): Promise<void>

参数说明forbidAllSpeak


kickMember(memberId)

踢出指定成员。

async kickMember(memberId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | memberId | string | 是 | 要踢出的成员用户 ID |


updateMeetingName(name)

修改会议名称。

async updateMeetingName(name: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | name | string | 是 | 新会议名称 |


updateMeetingPassword(password)

修改会议密码。

async updateMeetingPassword(password: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | password | string | 是 | 新密码 |


publishNotice(content)

发布公告到会议,所有成员都会收到。

async publishNotice(content: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | content | string | 是 | 公告内容 |


inviteUser(userId, message?)

邀请单个用户加入当前会议,可附带邀请文本信息。

async inviteUser(userId: string, message?: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | userId | string | 是 | 被邀请用户的 ID | | message | string | 否 | 邀请附带的文本信息,被邀请方可在 meeting:invited 事件中收到 |


inviteUsers(userIds, message?)

批量邀请多个用户加入当前会议,可附带统一的邀请文本信息。

async inviteUsers(userIds: string[], message?: string): Promise<InviteUsersResult>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | userIds | string[] | 是 | 被邀请用户的 ID 数组 | | message | string | 否 | 邀请附带的文本信息,所有被邀请方均可在 meeting:invited 事件中收到 |

返回值 Promise<InviteUsersResult> — 邀请结果,包含:

  • success: boolean — 是否成功
  • invitedCount: number — 成功邀请的用户数量
  • invitedUserIds: string[] — 实际被邀请的用户 ID 列表

使用示例

const result = await session.inviteUsers(
  ['user001', 'user002', 'user003'],
  '请参加下午3点的产品评审会'
)
console.log(`成功邀请 ${result.invitedCount} 人`)
console.log('被邀请用户:', result.invitedUserIds)

generateInviteLink(message?)

生成会议邀请链接,可附带邀请文本信息。链接默认 24 小时内有效。

async generateInviteLink(message?: string): Promise<{ inviteLink: string; expiresIn: number }>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | message | string | 否 | 邀请附带的文本信息,将嵌入到邀请令牌中,被邀请方验证链接时可见 |

返回值 Promise<{ inviteLink: string; expiresIn: number }> — 邀请链接和过期时间(秒)

等候室(主持人)


getWaitingMembers()

获取当前在等候室中的成员列表。

async getWaitingMembers(): Promise<WaitingMember[]>

返回值 Promise<WaitingMember[]> — 等候成员数组,每项包含:

  • userId: string — 用户 ID
  • endpointId: string — 端点 ID
  • deviceType: string — 设备类型
  • requestedAt: Date — 请求入会时间
  • userName: string — 昵称
  • avatar?: string — 头像

approveWaitingMember(userId, endpointId)

批准等候室中的成员入会。

async approveWaitingMember(userId: string, endpointId: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | userId | string | 是 | 用户 ID | | endpointId | string | 是 | 端点 ID |


rejectWaitingMember(userId, endpointId, reason?)

拒绝等候室中的成员入会。

async rejectWaitingMember(
  userId: string,
  endpointId: string,
  reason?: string
): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | userId | string | 是 | 用户 ID | | endpointId | string | 是 | 端点 ID | | reason | string | 否 | 拒绝原因 |


approveAllWaitingMembers()

一键批准所有等候室成员。

async approveAllWaitingMembers(): Promise<{ userId: string; endpointId: string }[]>

返回值 Promise<Array<{ userId, endpointId }>> — 被批准的成员列表


rejectAllWaitingMembers(reason?)

一键拒绝所有等候室成员。

async rejectAllWaitingMembers(reason?: string): Promise<{ userId: string; endpointId: string }[]>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | reason | string | 否 | 拒绝原因 |

返回值 Promise<Array<{ userId, endpointId }>> — 被拒绝的成员列表


setWaitingRoom(enabled)

开启或关闭等候室功能。

async setWaitingRoom(enabled: boolean): Promise<{ waitingRoom: boolean }>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | enabled | boolean | 是 | true 开启,false 关闭 |

返回值 Promise<{ waitingRoom: boolean }> — 当前等候室状态

会话生命周期


leave()

离开会议。调用后端接口并清理本地资源。离开后此会话实例不可再使用。

async leave(): Promise<void>

close()

关闭会议(仅主持人可用)。关闭后所有成员将被强制离开,此会话实例不可再使用。

async close(): Promise<void>

异常 调用者不是主持人时抛出权限错误


MediaClientNode

底层 mediasoup-client 封装类。通常不需要直接使用,由 MeetingSession 内部管理。在需要深度自定义 WebRTC 行为时使用。

import { MediaClientNode } from 'omnilink-client'

属性

| 属性 | 类型 | 说明 | |------|------|------| | isDeviceLoaded | boolean | 设备是否已加载,加载完成后才能创建传输 | | rtpCapabilities | types.RtpCapabilities | 本地设备的 RTP 能力参数,需在 initDevice() 后获取 |


initDevice(routerRtpCapabilities)

初始化 mediasoup Device。

async initDevice(routerRtpCapabilities: types.RtpCapabilities): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | routerRtpCapabilities | types.RtpCapabilities | 是 | 服务器端 RTP 能力,从后端 /api/v1/meeting/rtp-capabilities 获取 |


createSendTransport(options, onConnect, onProduce)

创建发送传输(用于推流)。

async createSendTransport(
  options: types.TransportOptions,
  onConnect: (transportId: string, dtlsParameters: types.DtlsParameters) => Promise<void>,
  onProduce: (transportId: string, kind: types.MediaKind, rtpParameters: types.RtpParameters, appData?: Record<string, unknown>) => Promise<string>
): Promise<types.Transport>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | options | types.TransportOptions | 是 | 传输配置(id、iceParameters、iceCandidates、dtlsParameters 等) | | onConnect | function | 是 | DTLS 连接回调。收到 dtlsParameters 时需要发送到服务端完成连接 | | onProduce | function | 是 | 生产回调。需要向服务端请求创建 Producer,返回服务端分配的 producerId |

返回值 Promise<types.Transport> — 发送传输对象


createRecvTransport(options, onConnect)

创建接收传输(用于拉流)。

async createRecvTransport(
  options: RecvTransportOptions,
  onConnect: (transportId: string, dtlsParameters: types.DtlsParameters) => Promise<void>
): Promise<types.Transport>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | options | RecvTransportOptions | 是 | 接收传输配置 | | onConnect | function | 是 | DTLS 连接回调 |

返回值 Promise<types.Transport> — 接收传输对象


produce(transport, options)

在发送传输上生产媒体。

async produce(transport: types.Transport, options: ProduceOptions): Promise<types.Producer>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | transport | types.Transport | 是 | 发送传输对象 | | options | ProduceOptions | 是 | 生产选项 | | options.track | MediaStreamTrack | 是 | 媒体轨道 | | options.appData? | Record<string, unknown> | 否 | 应用自定义数据 | | options.encodings? | types.RtpEncodingParameters[] | 否 | 编码参数 |

返回值 Promise<types.Producer> — mediasoup Producer 对象


produceVideo(sendTransport, videoTrack, appData?)

便捷方法:创建视频生产者。内部调用 produce()

async produceVideo(
  sendTransport: types.Transport,
  videoTrack: MediaStreamTrack,
  appData?: Record<string, unknown>
): Promise<types.Producer>

consume(data, appData?)

消费远端媒体流。

async consume(
  data: {
    transportId: string
    consumerId: string
    producerId: string
    kind: 'audio' | 'video'
    rtpParameters: types.RtpParameters
  },
  appData?: Record<string, unknown>
): Promise<{ consumerId: string; stream: MediaStream }>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | data.transportId | string | 是 | 接收传输 ID | | data.consumerId | string | 是 | 服务端分配的消费者 ID | | data.producerId | string | 是 | 远端生产者 ID | | data.kind | 'audio' \| 'video' | 是 | 媒体类型 | | data.rtpParameters | types.RtpParameters | 是 | RTP 参数 | | appData | object | 否 | 附加数据 |

返回值 Promise<{ consumerId: string; stream: MediaStream }>


closeProducer(producerId)

关闭指定生产者。

async closeProducer(producerId: string): Promise<void>

getProducer(id)

获取生产者实例。

getProducer(id: string): types.Producer | undefined

getConsumer(id)

获取消费者实例。

getConsumer(id: string): types.Consumer | undefined

getSendTransport(id)

获取发送传输实例。

getSendTransport(id: string): types.Transport | undefined

getRecvTransport(id)

获取接收传输实例。

getRecvTransport(id: string): types.Transport | undefined

getStats()

获取 WebRTC 统计信息。

async getStats(): Promise<{ transportId: string; direction: string; stats: RTCStatsReport }[]>

返回值 统计报告数组


close()

关闭所有传输并清理资源。调用后此实例不可复用,需重新创建 MediaClientNode

close(): void

HttpService

HTTP 请求封装层,基于 Axios。通常由内部服务使用,也可独立使用。

import { HttpService } from 'omnilink-client'

静态常量

| 常量 | 值 | 说明 | |------|-----|------| | SUCCESS_CODE | 200 | 成功状态码 | | UNAUTHORIZED_CODE | 401 | 未授权 | | FORBIDDEN_CODE | 403 | 禁止访问 | | NOT_FOUND_CODE | 404 | 未找到 | | SERVER_ERROR_CODE | 500 | 服务器错误 |


setTokenGetter(getter)

设置令牌获取函数。每次请求前会调用此函数获取 token。

setTokenGetter(getter: () => string | null): void

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | getter | () => string \| null | 是 | 返回 token 或 null 的函数 |


setTokenHeaderKey(key)

设置认证请求头字段名。默认 'Authorization'

setTokenHeaderKey(key: string): void

setPublicPaths(paths)

设置免认证路径列表。匹配这些路径的请求不会自动附加 token。

setPublicPaths(paths: string[]): void

addPublicPath(path)

添加单个免认证路径。

addPublicPath(path: string): void

setTimeout(timeout)

设置请求超时时间(毫秒)。默认 30000ms。

setTimeout(timeout: number): void

get(url, config?)

发送 GET 请求。

async get<T>(url: string, config?: AxiosRequestConfig): Promise<T>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | url | string | 是 | 请求地址 | | config | AxiosRequestConfig | 否 | Axios 请求配置 |

返回值 Promise<T> — 响应数据(已提取 response.data.data


post(url, data?, config?)

发送 POST 请求。

async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>

put(url, data?, config?)

发送 PUT 请求。

async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>

patch(url, data?, config?)

发送 PATCH 请求。

async patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>

delete(url, config?)

发送 DELETE 请求。

async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>

getAxiosInstance()

获取底层 Axios 实例,用于添加自定义拦截器等高级操作。

getAxiosInstance(): AxiosInstance

dispose()

释放资源。

dispose(): void

HttpError

HTTP 请求错误类,继承自 Error。所有通过 HttpService 发起的请求失败时都会抛出此错误。

import { HttpError } from 'omnilink-client'

属性

| 属性 | 类型 | 说明 | |------|------|------| | status | number | HTTP 状态码(如 401、403、500) | | code | number | 业务错误码(后端返回的业务 code) | | data | any | 错误响应的原始数据 | | url | string | 触发错误的请求地址 | | message | string | 错误信息(继承自 Error) | | name | string | 固定为 'HttpError' |


SocketManager

WebSocket 连接管理器,基于 Socket.IO。通常由 OmniLinkClient 内部管理,也可通过 client.socket 访问。


connect(url, token?)

建立 Socket.io WebSocket 连接。

async connect(url: string, token?: string): Promise<void>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | url | string | 是 | 服务器地址,如 'ws://localhost:3000' | | token | string | 否 | 用户认证 token |


disconnect(intentional?)

断开连接。

disconnect(intentional?: boolean): void

参数说明

| 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|:--:|--------|------| | intentional | boolean | 否 | true | 是否主动断开 |


isConnected()

是否已连接。

isConnected(): boolean

isConnecting()

是否正在连接中。

isConnecting(): boolean

getReconnectAttempts()

获取当前重连尝试次数。

getReconnectAttempts(): number

request(command, data?)

发送请求到服务器并等待响应。

async request(command: string, data?: any): Promise<any>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | command | string | 是 | 命令名称 | | data | any | 否 | 请求数据 |

返回值 Promise<any> — 服务器响应数据


notify(command, data?)

发送通知到服务器(无需响应)。

notify(command: string, data?: any): void

joinMeeting(token)

通过会议 token 加入会议房间。

async joinMeeting(token: string): Promise<any>

参数说明

| 参数 | 类型 | 必填 | 说明 | |------|------|:--:|------| | token | string | 是 | 会议 token |


leaveMeeting()

离开会议房间。

async leaveMeeting(): Promise<void>

getCurrentMeetingToken()

获取当前会议的 token。

getCurrentMeetingToken(): string | null

getMeetingMembers(meetingId)

通过 WebSocket 获取会议成员。

async getMeetingMembers(meetingId: string): Promise<any>

sendChat(content)

发送聊天消息。

sendChat(content: string): void

Logger

静态日志工具类,支持日志级别控制和源代码位置显示。

import { Logger, LogLevel } from 'omnilink-client'

在浏览器环境中可通过 window.OmniLinkLogger 直接访问。

enum LogLevel {
  DEBUG = 0,   // 最详细,输出所有日志
  INFO = 1,    // 关键流程节点(默认)
  WARN = 2,    // 警告信息
  ERROR = 3,   // 仅错误
}

setLevel(level)

设置日志级别。低于此级别的日志不会输出。

static setLevel(level: LogLevel): void

debug(...args)

输出 DEBUG 级别日志(蓝色)。

static debug(...args: any[]): void

info(...args)

输出 INFO 级别日志(绿色)。

static info(...args: any[]): void

warn(...args)

输出 WARN 级别日志(橙色)。

static warn(...args: any[]): void

error(...args)

输出 ERROR 级别日志(红色)。

static error(...args: any[]): void

媒体设备辅助函数

import { listVideoInputDevices, listAudioInputDevices } from 'omnilink-client'

listVideoInputDevices()

列出所有视频输入设备(摄像头)。

注意:需要先获取摄像头权限,否则设备标签可能为空字符串。

async listVideoInputDevices(): Promise<MediaDeviceInfo[]>

返回值 Promise<MediaDeviceInfo[]> — 视频设备列表。获取失败时返回空数组。


listAudioInputDevices()

列出所有音频输入设备(麦克风)。

注意:需要先获取麦克风权限,否则设备标签可能为空字符串。

async listAudioInputDevices(): Promise<MediaDeviceInfo[]>

返回值 Promise<MediaDeviceInfo[]> — 音频设备列表。获取失败时返回空数组。


类型定义

SDK 导出了以下 TypeScript 类型,可通过 import type { ... } from 'omnilink-client' 使用。

配置

| 类型 | 说明 | |------|------| | OmniLinkClientConfig | 客户端配置,{ serviceUrl?, userServiceUrl?, meetingServiceUrl?, socketUrl?, basePath? } | | OmniLinkClientConfigResolved | 解析后的完整配置 | | ReconnectConfig | 重连配置,{ enabled, maxAttempts, initialDelay, maxDelay, backoffMultiplier } | | ReconnectState | 重连状态 | | ReconnectingEvent | 重连事件数据,{ attempt, maxAttempts, delay } |

用户

| 类型 | 说明 | |------|------| | User | 用户信息,{ id, originalId, name, avatar?, email?, createdAt } | | LoginCredentials | 登录凭证,{ originalId, password, deviceType, deviceId, userAgent?, force? } | | LoginResult | 登录结果,{ token, userId, endpointId, session, endpoints } | | RegisterData | 注册数据,{ originalId, username, password, email? } | | EndpointInfo | 端点信息 | | LoginResponse | 登录响应联合类型(成功/失败) | | ApiResponse | 通用 API 响应 | | ApiKeyExchangeCredentials | API Key 交换凭证,{ apiId, apiKey, originalId } | | ApiKeyExchangeResult | API Key 交换结果,{ preLoginToken, expiresIn, userId, userName } | | ApiKeyLoginCredentials | API Key 登录凭证,{ preLoginToken, deviceType, deviceId, userAgent?, force? } | | UserBasicInfo | 用户基础信息(脱敏),{ userId, originalId, name, avatar? } |

会议

| 类型 | 说明 | |------|------| | Meeting | 会议详情,包含名称、创建者、密码、等候室开关、状态等 | | MeetingMember | 会议成员,{ userId, userName, avatar?, role, joinedAt, online?, endpoints?, hasAudio?, forbidden? } | | WaitingMember | 等候室成员,{ userId, endpointId, deviceType, requestedAt, userName, avatar? } | | CreateMeetingData | 创建会议参数,{ meetingName?, visitorAllowed?, memberInviteAllowed?, multiEndpoint?, needPassword?, allowScreenShare?, allowRecording?, waitingRoom?, muteOnEntry?, startAt?, duration?, notice?, inviteMembers? } | | JoinMeetingData | 加入会议参数,{ meetingId, password, deviceType?, force? } | | MeetingJoinResult | 加入会议结果,{ meeting, token } | | MeetingListResult | 会议列表分页结果,{ meetings, total, page, pageSize, totalPages } | | PaginationParams | 分页参数,{ page?, pageSize? } | | MeetingClosedEvent | 会议关闭事件 | | KickedFromMeetingEvent | 被踢出会议事件 |

WebRTC / 媒体

| 类型 | 说明 | |------|------| | RtpCapabilities | RTP 能力参数 | | IceCandidate | ICE 候选地址 | | DtlsParameters | DTLS 参数 | | IceParameters | ICE 参数 | | TransportOptions | 传输配置选项 | | ProducerInfo | 生产者信息,{ id, kind, appData? } | | ConsumerInfo | 消费者信息 | | MediaTrackInfo | 媒体轨道信息 | | DeviceConstraints | 设备约束 | | MediaDeviceInfo | 媒体设备信息 |

事件

| 类型 | 说明 | |------|------| | MemberInfo | 成员基础信息,{ userId, userName, role, avatar?, online? } | | MemberJoinedEvent | 成员加入事件(继承 MemberInfo) | | MemberLeftEvent | 成员离开事件,{ userId } | | MembersUpdatedEvent | 成员列表更新事件,{ members: MemberInfo[] } | | ProducerEvent | 生产者事件,{ producerId, kind, userId?, appData? } | | ConsumerEvent | 消费者事件 | | UserKickedEvent | 用户被踢事件 | | UserConnectedEvent | 用户连接事件 | | PermissionsChangedEvent | 权限变更事件,{ meetingId, permissions: { video?, audio?, screen?, text? } } | | SocketEventMap | Socket 事件映射 | | EventName | 事件名称类型 |

聊天 / 消息

| 类型 | 说明 | |------|------| | ChatMessage | 聊天消息,{ id?, senderId, senderName, avatar?, content, time, type?, fileUrl?, fileName?, fileSize? } | | ChatMessageEvent | 聊天消息事件 | | NoticePublishedEvent | 公告发布事件 | | SystemNotification | 系统通知 |

录制

| 类型 | 说明 | |------|------| | RecordingOptions | 录制选项 | | RecorderControl | 录制控制器 | | RecordingResult | 录制结果 |

完整示例

通过 API Key 登录(企业 SSO / 第三方接入)

适用于已有统一身份认证系统的企业,或第三方应用无需密码直接接入 OmniLink 的场景。

⚠️ 安全警告apiKeysk_ 开头)等同于密码,严禁暴露到前端代码或浏览器中。它必须由第三方企业服务端安全保管,仅用于服务端调用 exchange 接口。

┌──────────────────┐         ┌─────────────────────┐         ┌──────────────────┐
│   第三方企业前端   │         │   第三方企业服务端   │         │   OmniLink 后端   │
│   (浏览器/App)   │         │   (Java/Python/Go)  │         │                  │
│                  │         │                     │         │                  │
│  ① 用户在企业门户 │         │  ② 服务端调用 exchange │         │                  │
│     点击单点登录   │  ────>  │     {apiId,apiKey}  │  ────>  │  POST /apikey/exchange
│                  │         │                     │         │                  │
│  ④ 收到preLoginToken│  <────  │  ③ 返回preLoginToken │  <────  │  返回预登录令牌    │
│                  │         │                     │         │                  │
│  ⑤ 调用loginWithApiKey│ ────> │  (透传或直接代理)   │  ────>  │  POST /apikey/login
│                  │         │                     │         │                  │
│  ⑥ 登录成功,自动   │         │                     │         │  返回标准 JWT     │
│     连接WebSocket  │         │                     │         │                  │
└──────────────────┘         └─────────────────────┘         └──────────────────┘

Step 1 — 第三方企业服务端调用 exchange(任意语言):

# cURL
curl -X POST https://omnilink.example.com/api/v1/apikey/exchange \
  -H "Content-Type: application/json" \
  -d '{
    "apiId": "ak_c18d803d05e20348",
    "apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
    "originalId": "zhangsan"
  }'
# Python 示例
import requests

res = requests.post("https://omnilink.example.com/api/v1/apikey/exchange", json={
    "apiId": "ak_c18d803d05e20348",
    "apiKey": "sk_edff3405842fc748efe69983a68516c9d9cdcd388f7505a9",
    "originalId": "zhangsan"
})
data = res.json()["data"]
pre_login_token = data["preLoginToken"]
# 将 pre_login_token 返回给前端页面
// Java (OkHttp) 示例
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(
    MediaType.parse("application/json"),
    "{\"apiId\":\"ak_c18d803d05e20348\",\"apiKey\":\"sk_...\",\"originalId\":\"zhangsan\"}"
);
Request request = new Request.Builder()
    .url("https://omnilink.example.com/api/v1/apikey/exchange")
    .post(body)
    .build();
Response response = client.newCall(request).execute();
// 解析 response 获取 preLoginToken,返回给前端

Step 2 — 前端用 preLoginToken 完成登录(omnilink-client SDK):

import { OmniLinkClient } from 'omnilink-client'

async function ssoLogin(preLoginToken: string) {
  const client = new OmniLinkClient({ serviceUrl: 'https://omnilink.example.com' })

  try {
    const loginResult = await client.loginWithApiKey({
      preLoginToken,                       // 来自企业服务端
      deviceType: 'web',
      deviceId: 'enterprise-portal-browser-001',
      userAgent: 'EnterprisePortal/2.0'
    })

    console.log('登录成功', loginResult.userId)
    console.log('当前会话:', loginResult.session)
    console.log('在线设备列表:', loginResult.endpoints)
  } catch (err: any) {
    if (err.needForce) {
      // 同一设备类型已有会话,强制踢掉旧会话
      console.warn('账号已在其他设备登录,准备强制登录...')
      const loginResult = await client.loginWithApiKey({
        preLoginToken,
        deviceType: 'web',
        deviceId: 'enterprise-portal-browser-001',
        force: true
      })
      console.log('强制登录成功', loginResult.userId)
    } else {
      throw err
    }
  }

  // 获取当前用户信息
  const user = await client.getProfile()
  console.log('用户信息:', user.name, user.email)

  // 监听被踢事件
  client.onKickedFromServer((data) => {
    console.warn('已被踢下线:', data.reason, '设备:', data.deviceId)
  })

  // 创建并加入会议
  const session = await client.createAndJoinMeeting({
    meetingName: '企业周例会',
    waitingRoom: false,
    duration: 60,  // 1小时后自动结束
  })

  console.log('会议已创建:', session.meetingId)
  return { client, session }
}

流程说明:

  1. 企业服务端调用 POST /api/v1/apikey/exchange — 用 apiId + apiKey + userId 换取短期 preLoginToken(默认 5 分钟)。支持任意编程语言,不依赖 Node.js。
  2. 企业服务端将 preLoginToken 返回给前端 — 可通过 SSR 注入页面、JSON API 响应、或 cookie 等方式传递。
  3. 前端调用 client.loginWithApiKey() — 将 preLoginToken + 设备信息发送给 OmniLink 后端,完成登录并获取标准 JWT。
  4. 登录成功后 SDK 自动保存 token 到 localStorage,并自动建立 WebSocket 连接。
  5. 如遇 409 冲突(同一 deviceType 已有活跃会话),可传 force: true 强制踢掉旧会话。

创建会议并分享屏幕

import { OmniLinkClient } from 'omnilink-client'

async function startScreenShareMeeting() {
  const client = new OmniLinkClient({ serviceUrl: 'https://api.example.com' })

  // 登录
  await client.login({
    originalId: '[email protected]',
    password: 'password',
    deviceType: 'pc',
    deviceId: 'desktop-01'
  })

  // 创建会议
  const session = await client.createAndJoinMeeting({
    meetingName: '项目评审会',
    waitingRoom: true,
    duration: 90,  // 1.5小时后自动结束
  })

  console.log(`会议ID: ${session.meetingId}`)
  console.log(`密码: ${session.password}`)

  // 初始化媒体
  await session.initMediaClient()

  // 获取屏幕共享流
  const screenStream = await navigator.mediaDevices.getDisplayMedia({
    video: true,
    audio: true
  })

  // 发布屏幕共享
  const screenTrack = screenStream.getVideoTracks()[0]
  await session.produceTrack(screenTrack, { kind: 'screen' })

  // 监听远端流
  client.onProducerCreated(async ({ producerId, kind, userId }) => {
    if (kind === 'video' || kind === 'screen') {
      const { stream } = await session.consume(producerId, kind)
      createVideoElement(userId, stream)
    }
  })

  // 监听聊天
  client.onChatMessage((msg) => {
    appendMessage(msg.senderName, msg.content)
  })
}

function createVideoElement(userId: string, stream: MediaStream) {
  let el = document.getElementById(`video-${userId}`) as HTMLVideoElement
  if (!el) {
    el = document.createElement('video')
    el.id = `video-${userId}`
    el.autoplay = true
    document.getElementById('video-grid')!.appendChild(el)
  }
  el.srcObject = stream
}

function appendMessage(name: string, content: string) {
  const div = document.createElement('div')
  div.textContent = `${name}: ${content}`
  document.getElementById('chat-list')!.appendChild(div)
}

通过邀请链接加入会议

import { OmniLinkClient } from 'omnilink-client'

async function joinByInvite(inviteToken: string) {
  const client = new OmniLinkClient({ serviceUrl: 'https://api.example.com' })

  // 先登录
  await client.login({
    originalId: '[email protected]',
    password: 'password',
    deviceType: 'pc',
    deviceId: 'desktop-02'
  })

  // 验证邀请链接
  const verify = await client.meeting.verifyInviteLink(inviteToken)
  if (!verify.valid) {
    throw new Error('邀请链接已失效')
  }

  // 通过邀请链接加入
  const session = await client.meeting.joinByInviteLink(inviteToken)

  // 初始化媒体
  await session.initMediaClient()

  // 发布摄像头
  const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
  await session.produceTrack(stream.getVideoTracks()[0], { kind: 'video' })
  await session.produceTrack(stream.getAudioTracks()[0], { kind: 'audio' })

  return session
}

恢复已加入的会议

// 页面刷新后恢复会议
async function restoreMeeting(client: OmniLinkClient) {
  const restored = await client.restoreSession()
  if (!restored) {
    console.log('会话已过期,需要重新登录')
    return
  }

  if (client.isInMeeting()) {
    const session = client.getCurrentSession()
    if (session) {
      // 重新初始化媒体
      await session.initMediaClient()

      // 重新发布音视频
      const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
      await session.produceTrack(stream.getVideoTracks()[0], { kind: 'video' })
      await session.produceTrack(stream.getAudioTracks()[0], { kind: 'audio' })
    }
  }
}

等候室场景

// 主持人端:管理等候室
client.onEvent('meeting:waiting-member', async (member) => {
  console.log(`等候室新成员: ${member.userName}`)

  // 批准入会
  await session.approveWaitingMember(member.userId)

  // 或拒绝入会
  // await session.rejectWaitingMember(member.userId)
})

// 开启/关闭等候室
await session.setWaitingRoom(true)   // 开启
await session.setWaitingRoom(false)  // 关闭

配置选项

OmniLinkClientConfig

interface OmniLinkClientConfig {
  serviceUrl?: string        // 统一服务地址(推荐)
  userServiceUrl?: string    // 用户服务地址(兼容旧版)
  meetingServiceUrl?: string // 会议服务地址(兼容旧版)
  socketUrl?: string         // WebSocket 连接地址(当与 HTTP 使用不同路径时设置)
  basePath?: string          // 前端部署子路径,如 '/omnilink/web'
}

错误处理

SDK 中的异步方法失败时会抛出错误,建议统一包裹 try/catch

try {
  await client.login(credentials)
} catch (err: any) {
  if (err.code === 409) {
    // 账号已在其他设备登录
    const force = confirm('账号已在其他设备登录,是否强制登录?')
    if (force) {
      await client.login({ ...credentials, force: true })
    }
  } else {
    alert(err.message)
  }
}

浏览器兼容性

  • Chrome 74+
  • Edge 79+
  • Firefox 67+
  • Safari 14+

需要浏览器支持 getUserMediaRTCPeerConnection

更新日志

v2.1.1(2026-06-11)

重点修复:音频约束显式启用回声消除与噪声抑制

  • getAudioTrack() 方法现在显式启用 echoCancellation: true,解决两台设备物理距离较近时打开音频产生的啸叫问题
  • 同时启用 noiseSuppression: trueautoGainControl: true,降低环境噪声并自动限制麦克风增益
  • 旧版本仅传 audio: true,依赖浏览器默认策略,部分环境下回声消除未生效

v2.1.0(2026-06-10)

重点更新:新增 socketUrlbasePath 配置

  • 新增 socketUrl 配置项,支持 HTTP API 和 WebSocket 使用不同基础地址
  • 新增 basePath 配置项,支持前端子路径部署(如 /omnilink/web
  • 修复子路径部署下的 socket.io Invalid namespace 问题

v2.0.10(2026-04-27)

重点更新:新增通过 originalId 查询用户接口

  • 新增 client.user.getUserByOriginalId(originalId) 方法,支持通过外部系统用户登录名查询单个用户的基础信息
  • 新增 client.user.getUsersByOriginalIds(originalIds) 方法,支持批量查询多个用户的基础信息
  • 新增类型 UserBasicInfo,包含 userIdoriginalIdnameavatar? 字段
  • 两个接口均为公开接口,无需 Token 即可调用,适用于第三方系统查询 OmniLink 用户映射关系的场景
  • 后端对应接口:GET /api/v1/user/by-original-id/:originalIdPOST /api/v1/user/batch-by-original-ids

v2.0.9(2026-04-24)

重点更新:邀请功能支持附带文本信息

  • session.inviteUser(userId, message?) 新增可选 message 参数,支持邀请单个用户时附带邀请语
  • session.inviteUsers(userIds, message?) 新增可选 message 参数,支持批量邀请时统一附带邀请语
  • session.generateInviteLink(message?) 新增可选 message 参数,支持生成邀请链接时嵌入邀请语
  • MeetingInvitedEvent 新增 message?: string 字段,被邀请方可通过 client.onMeetingInvited() 接收邀请语
  • 邀请信息会持久化到会议成员记录的 inviteMessage 字段中

v2.0.8(2026-04-23)

重点更新:创建会议支持预约时间和时长

  • CreateMeetingData 新增 startAt(会议开始时间,时间戳)和 duration(会议时长,分钟)参数
  • 支持预约未来时间开始的会议
  • 设置 duration 后,会议将在到期后自动结束,期间所有用户退出也不会提前关闭
  • 适用于需要严格控制会议时长的场景(如限时面试、定时培训等)

v2.0.7(2026-04-23)

重点更新:API Key 登录参数调整为 originalId

  • exchange 接口请求参数从 userId(MongoDB ObjectId)改为 originalId(用户登录名)
  • 第三方企业系统无需知道 OmniLink 内部数据库 ID,直接使用用户的登录名即可调用
  • 所有文档示例已同步更新

v2.0.6(2026-04-23)

重点更新:新增 API Key 登录支持

  • 新增 client.loginWithApiKey(credentials) 方法,支持通过预登录令牌完成登录
  • 新增 client.user.exchangeApiKey(credentials) 方法,支持用 API 凭证交换预登录令牌
  • 新增类型:ApiKeyExchangeCredentialsApiKeyExchangeResultApiKeyLoginCredentials
  • 适用于第三方应用服务端、企业 SSO、自动化脚本等无密码登录场景
  • 完整的两步登录流程:exchange → preLoginToken → login → JWT

v2.0.5(2026-04-23)