跳转到内容

API 参考

new LiveSDK(config: SDKConfig): LiveSDK

必填字段缺失时抛出异常activityIdtokenmerchantId

interface SDKConfig {
/** 直播间 ID */
activityId: string
/** JWT 访问令牌(由 auth 服务颁发) */
token: string
/** 商户 ID */
merchantId: string
/** 门店 ID;房间营销 cmd 6010 上报透传,缺省按 0 */
storeId?: number
/** HTTP API host;运行时优先,缺失时才读取 VITE_API_ENDPOINT */
api?: SDKApiConfig
/** 统一端点;优先于兼容字段与构建环境变量 */
endpoints?: EndpointConfig
/** 可信后端签发的观看会话交接上下文;缺失时可靠任务上报保持关闭 */
watchSession?: WatchSessionConfig
/** mount 认证的房间详情启动策略;未配置时保持 SDK 自拉详情 */
bootstrap?: SDKBootstrapConfig
/** BCP-47 locale 字符串,用于 UI 文案。默认 'zh-CN'。未知值警告后回退。 */
locale?: string
/** Phase 11:启用哪些 WS 驱动入口模块。默认全部启用。NEEDS-SERVER-VERIFY */
entryModules?: AppEntryModuleId[]
/** 腾讯 IM 配置(Phase 7) */
tim?: TIMConfig
/** @deprecated 改用 ws.url */
wsUrl?: string
/** LAPS WebSocket 配置(Phase 10)。未配置时降级为 HTTP 轮询。 */
ws?: WSConfig
/** 启用/禁用特定业务模块(默认全部启用) */
modules?: ModuleConfig[]
/** 内置 app 模块(ViewerCount / Like / Gift / Goods / Security)的开关 */
appModules?: AppModuleConfigs
/** 布局配置(横竖屏、Tab、模块顺序)*/
layout?: LayoutConfig
/**
* 业务功能配置(FR-FC-02)—— 业务 UI 可见性统一收口(按钮/入口是否渲染)。
* 与 appModules(WS 数据模块开关)正交。运行时可经 updateFeatures/setFeature 增量更新。
* 零配置 = 今日行为。详见 FeatureConfig。
*/
features?: FeatureConfig
options?: SDKOptions
/**
* 宿主注入 IM 用户资料(昵称 / 头像)。
* 等级 levelId 由 auth 返回的 ve/token.agentLevelId 自动写入,宿主不传。
*/
user?: { nickName?: string, avatar?: string }
/**
* 宿主注入的能力 handler(抛出给宿主实现)。
* 当前仅 redPacket(微信/账户写操作);未注入对应 handler 时 SDK 弹窗优雅降级。
*/
handlers?: {
/** 我的红包:提现 / 待收款确认(微信 JSAPI 由宿主处理) */
redPacket?: RedPacketHandlers
}
}
interface SDKApiConfig {
/** 协议 + host;内部会 trim 并移除尾斜杠 */
baseUrl?: string
}
interface RoomDetailBootstrap {
source: 'host-cache'
activityId: string
merchantId: string
detail: RoomDetailBody
}
interface SDKBootstrapConfig {
/** 默认 sdk-fetch;required 时 seed 缺失或非法也不回退请求详情 */
roomDetailPolicy?: 'sdk-fetch' | 'required'
roomDetail?: RoomDetailBootstrap
}

EndpointConfig{ apiBaseUrl?, wsUrl?, watchReportBaseUrl? }。解析顺序为 setEndpoints()SDKConfig.endpointsapi.baseUrl/ws.url/wsUrl → 构建变量。ordinary/Range 使用各自 SDK 内置 path,拼接时保留 SaaS base pathname;拒绝跨域、query/hash、凭据及路径穿越。setEndpoints(partial):Promise<this> 可在 mount 前后调用;getEndpoints() 返回当前规范化快照。

WatchSessionConfig{ watchSessionId, predecessorSessionId?, handoffCapability, predecessorFinalization },capability 为 'fence_v1'|'none',finalization 为 'successor_authorized'|'current_session_only'。该对象必须由可信登录/bootstrap 服务签发,浏览器不得自行生成 predecessor。

API 缺失时 fail-closed,WS 缺失时降级轮询,上报地址缺失时不发送。HTTP/上报仅接受 https://,WS 仅接受 wss://;输入会 trim 并移除尾 /

红包 handler(SDKConfig.handlers.redPacket)

Section titled “红包 handler(SDKConfig.handlers.redPacket)”

「我的红包」弹窗自包含余额展示与红包/提现记录列表(SDK 自调账户域 HTTP,仅需 token); 微信 JSAPI 写操作(提现申请状态机、授权绑定、待收款二次确认)整体抛给宿主实现。 SDK 调用后 await 返回归一化结果,据此自动刷新余额与提现记录。

interface RedPacketHandlers {
/** 发起提现(含可能的微信授权绑定子流程,宿主全权处理)。全额提现,amount 由 SDK 裁剪(≤200)。 */
withdraw?: (input: { amount: number }) => Promise<{
status: 'success' | 'cancelled' | 'fail' | 'pending' // pending=已提交待微信到账
message?: string // fail 时展示给用户
}>
/** 待收款二次确认(提现记录里 WAIT_USER_CONFIRM 状态的「待收款」按钮,对应微信 onConfirmPay)。 */
confirmReceipt?: (record: RedWithdraw) => Promise<{ status: 'success' | 'cancelled' | 'fail' | 'pending', message?: string }>
}

未注入 withdraw → 提现按钮禁用 + 提示「未配置提现能力」;明细列表仍可用。 未注入 confirmReceipt → 提现记录「待收款」按钮不可点。 金额规则:余额 <0.03 拦截不发;>200 裁剪为本次 200(微信单次上限)。

const sdk = new LiveSDK({
activityId, token, merchantId,
handlers: {
redPacket: {
withdraw: async ({ amount }) => {
// 宿主实现:applyWithdrawV2 + onConfirmPay + sync 状态机
// …
return { status: 'success' }
},
confirmReceipt: async (record) => {
// 宿主实现:onConfirmPay(record)
return { status: 'success' }
},
},
},
})

也可命令式打开:sdk.openRedPacket()(等价点击「我的红包」按钮)。

mount 时 SDK 将 config.user 与 JWT 昵称合并后同步到腾讯 IM(updateMyProfile),发评时在 cloudCustomData 附带 { levelId }(来自 ve/token.agentLevelId,无则为 null)。收端用 memberLevels 匹配展示等级徽章。

昵称优先级:config.user.nickName / 运行时 setProfile.nickName > JWT resolveImUserNickName > userId

头像:宿主 config.user.avatar / setProfile.avatar 优先;空或未传时回退 DEFAULT_AVATARhttps://image.jbzyun.cn/statics/image/def-avatar.png),避免 IM profile / 评论区空头像。

const sdk = new LiveSDK({
activityId, token, merchantId,
user: { nickName: '小明', avatar: 'https://cdn.example.com/avatar.png' },
})
await sdk.mount('#live')
// 进房后动态更新
await sdk.getModule<User>('user')?.setProfile({ nickName: '新昵称', avatar: 'https://...' })

updateMyProfile 失败仅 console.warn,不阻塞发评;levelId 不可由宿主注入。

interface TIMConfig {
sdkAppId: number // 腾讯 IM App ID
userId: string // 观众用户 ID
userSig: string // 服务端生成的 UserSig
groupId: string // 直播间 IM 群组 ID
}

注意:TIM 凭证由集成方通过环境变量或运行时注入,auth API 不返回这些字段

interface WSConfig {
url: string // wss://your-laps-host/ws
reconnectBaseDelay?: number // 重连基础延迟 ms(默认 1000)
reconnectMaxDelay?: number // 重连最大延迟 ms(默认 30000)
reconnectMaxAttempts?: number // 最大重连次数(默认 10)
heartbeatInterval?: number // 心跳间隔 ms(默认 25000)
authTimeout?: number // 认证超时 ms(默认 5000)
loginRetryMax?: number // 认证重试次数(默认 3)
}

注意:WS 端点由集成方提供,auth API 不返回 WS URL。解析顺序为 ws.url → 已废弃的 wsUrl → 构建期 VITE_WS_URL;均未配置时不连接 WS、降级 HTTP 轮询,没有内置测试地址兜底。

interface SDKOptions {
origin?: string // 保留字段;当前不参与 HTTP 地址解析,请使用 SDKConfig.api.baseUrl
saveUserInfo?: boolean
logLevel?: 'silent' | 'error' | 'warn' | 'info' | 'debug' // 默认 'warn'
/** 主题覆盖 — 仅传入字段覆盖 CSS 默认值 */
theme?: SDKTheme
/** 运行时 i18n 字符串覆盖,叠加在 locale 基础字符串之上 */
i18n?: Partial<I18nMessages>
/**
* image-text-float 浮窗图标可见性锁。
* 受控 key:QUESTION / QUESTIONNAIRE / CHECKIN / LOTTERY / LUCKYMONEY / COUPON。
* 未配置或空数组默认可见;key 支持可选 SYS: 前缀。
*/
floatingInteractionIcons?: { key: string, visible?: boolean }[]
/** 商品营销行为;默认 false 以兼容仍从 goods.promotion.click 自开的宿主 */
goodsMarketing?: {
autoOpenExchangeCoupon?: boolean
}
}
interface SDKTheme {
primaryColor?: string // 主色(按钮、高亮)
backgroundColor?: string // 背景色
overlayColor?: string // 浮层背景色
chatBgColor?: string // 聊天区域背景色
textColor?: string // 文字颜色
fontFamily?: string // 字体族
fontSize?: string // 基础字号(如 '14px')
radius?: string // 圆角(如 '8px')
}

CSS 变量以 --live-* 前缀注入,默认值定义在 .livesdk-root(app.css)中。

interface LayoutConfig {
/**
* 方向控制。
* - 'portrait' | 'landscape' → 强制指定,忽略 API 返回值
* - 'auto'(默认)→ 读取 body.extendSet.screenType(1=横屏,2=竖屏)
*/
orientation?: 'portrait' | 'landscape' | 'auto'
chatTabs?: { key: string; label: string }[]
modulesOrder?: { chatOffset?: number }
}
type AppEntryModuleId =
| 'roomMarketing' // 房间营销统一入口(签到 / 福袋 / 券)
| 'lottery'
| 'taskReward'
| 'productList'
| 'commentary'
| 'goodsMarketing'

⚠️ 旧 coupon / imageTextFloat / checkIn entry 已废止,统一并入 roomMarketing

FeatureConfig(业务特性配置,FR-FC-01)

Section titled “FeatureConfig(业务特性配置,FR-FC-01)”

业务 UI 可见性根类型。namespace 分组(topbar / goods / more),leaf key = 事件键去首段命名空间(保留后续点)。零配置 = 今日行为。

interface FeatureConfig {
/** TopBar 右上角按钮组可见性 */
topbar?: {
/** 个人中心入口(emit topbar.userCenter)。默认 true */
userCenter?: FeatureItemOptions
/** 经营信息入口(emit topbar.businessInfo)。默认回退 liveStore.businessInfoToggle */
businessInfo?: FeatureItemOptions
[key: string]: FeatureItemOptions | undefined
}
/** 商品橱窗可见性 */
goods?: {
/** 橱窗「购物车」入口(emit goods.cart.open)。默认 true */
'cart.open'?: FeatureItemOptions
/** 列表卡「加入购物车」按钮(emit goods.cart.add,非积分商品)。默认 true */
'cart.add'?: FeatureItemOptions
/** 橱窗「订单」入口(emit goods.order.open)。默认 true */
'order.open'?: FeatureItemOptions
[key: string]: FeatureItemOptions | undefined
}
/** 「更多」面板入口可见性 */
more?: {
/** 清晰度入口 + 底栏更多下标(emit more.quality)。默认 true;仍受多档流 + DefinitionPlugin 门控 */
quality?: FeatureItemOptions
/** 字体大小入口(emit more.fontSize)。默认 true */
fontSize?: FeatureItemOptions
/** 清屏入口(emit more.cleanScreen)。默认 true */
cleanScreen?: FeatureItemOptions
/** 我的红包入口(emit more.redPacket)。默认 true */
redPacket?: FeatureItemOptions
/** 我的积分入口(emit more.score)。默认 true */
score?: FeatureItemOptions
/** 我的券码入口(emit more.coupon)。默认 true */
coupon?: FeatureItemOptions
/** 我的订单入口(emit more.order)。默认 true */
order?: FeatureItemOptions
/** 意见/反馈入口(emit more.feedback)。默认 true */
feedback?: FeatureItemOptions
[key: string]: FeatureItemOptions | undefined
}
[namespace: string]: unknown
}
interface FeatureItemOptions {
/** UI 可见性。false 隐藏入口;未设 → 回退下一优先级层 */
enabled?: boolean
/** 预留排序位(本次不消费) */
order?: number
/** 前向兼容扩展槽 */
[key: string]: unknown
}
/** 可配置业务特性键 = 该功能激活时 emit 的精确事件键(C-2) */
type FeatureKey =
| 'topbar.userCenter'
| 'topbar.businessInfo'
| 'goods.cart.open'
| 'goods.cart.add'
| 'goods.order.open'
| 'more.quality'
| 'more.fontSize'
| 'more.cleanScreen'
| 'more.redPacket'
| 'more.score'
| 'more.coupon'
| 'more.order'
| 'more.feedback'

可见性优先级:运行时(setFeature/updateFeatures)> 构造期 config.features > 服务端兜底/默认。


// 挂载 Svelte UI 到 DOM 容器
sdk.mount(target: string | HTMLElement): Promise<void>
// 销毁 SDK,卸载 UI,清理所有资源
sdk.destroy(): void
// 事件系统(由 SDK_EVENTS 类型化)
sdk.on(event, handler): this
sdk.off(event, handler): this
sdk.once(event, handler): this
// 当前校准后的服务端 epoch 毫秒;未获得有效服务端锚点时返回 undefined
sdk.getServerTime(): number | undefined
// 本机真实媒体观看快照;服务端任务累计值不会覆盖它
sdk.getWatchProgress(): Readonly<WatchProgressSnapshot>
// 宿主手动注入房间配置时,serverTime 会重校时同一实例 ServerClock
sdk.stores.live?.setRoomConfig({ serverTime: 1_700_000_000_000 })
// 插件系统
sdk.use(plugin: IPlugin): this
// 业务模块访问
sdk.getModule<T>(id: ModuleId): T | undefined
// 应用层入口模块访问(Phase 11,NEEDS-SERVER-VERIFY)
sdk.getAppModule(id: AppEntryModuleId): BaseAppModule | undefined
// i18n 文案(公共方法,供宿主页面使用)
sdk.t(key: I18nKey): string
// 业务特性配置(FR-FC-06)—— 业务 UI 可见性运行时增量更新,链式返回 this,mount 前可用
// 深合并 namespace→leaf 两级;emit features.update {partial}(setFeature 带 key)
sdk.updateFeatures(partial: FeatureConfig): this
sdk.setFeature(key: FeatureKey, options: FeatureItemOptions): this // key = 对应事件键
// 当前特性配置快照(static+runtime 合并,不含服务端兜底)
sdk.getFeatures(): Readonly<FeatureConfig>
// 轻量 toast(小程序 wx.showToast 风格,独立于 svelte-sonner 的 sdk.toast)
// 全局单例:再次调用仅替换 content 并重置计时(不堆叠)。链式返回 this
sdk.showToast(opts: { content: string; duration?: number; mask?: boolean }): this
// duration 默认 5000ms;<=0 不自动关闭。mask=true 渲染透明蒙层拦截底层点击(默认 false 穿透)
// 最多两行 / 最大宽 90% / 容器内顶层(仅覆盖 SDK 容器,z=100000)。内部核心层亦可 this.showToast() 调用
sdk.hideToast(): this

getServerTime() 以实例为作用域。首次 mount() 认证完成后由 config/detail.serverTime 播种,运行中由 LAPS serverTimews.room_config.serverTime 持续重校时;房间营销 HTTP 顶层 timestamp 可在未锚定时补充。秒/毫秒自动判位,锚定后只按 performance.now() 的单调增量推进,因此修改设备日期或时区不会改变结果;重复或乱序响应也不会让时间倒退。尚未同步时返回 undefined,调用方应等待 ready/实时帧或隐藏依赖服务端时间的 UI,不要回退 Date.now()

const serverNow = sdk.getServerTime()
if (serverNow !== undefined) {
const remainMs = Math.max(0, deadlineMs - serverNow)
}

事件名 Payload 说明
ready undefined SDK 初始化完成
destroy undefined SDK 销毁
error { code: number, message: string } 全局错误
事件名 Payload 说明
live.status LiveStatus 直播状态变更
live.orientation 'portrait' | 'landscape' 布局方向确定(取自 extendSet.screenType + config 覆盖)

LiveStatus 枚举:

服务端状态码 说明
'not_started' 102 预告
'ongoing' 101 直播中
'ended' 103 已结束
'banned' 104 禁播
'paused' 105 暂停
'error' 106 异常
'expired' 107 已过期
'playback' 108 回放(VOD)
事件名 Payload 说明
player.ready undefined TCPlayer 就绪
player.playing undefined 开始播放
player.pause undefined 暂停
player.error { code: number, message: string } 播放错误
player.duration { duration: number } 当前视频/全局回放轴总时长(秒)
player.timeupdate { currentTime: number } 当前播放位置(TCPlayer timeupdate,节流 250ms)
player.quality.changed { code: string, name: string } 兼容事件:目标档位状态改变,不保证目标流已出帧
player.quality.switching { switchId, fromCode, toCode, name, reason } 切流事务开始;当前源继续播放,目标源开始隐藏预加载
player.quality.switched { switchId, code, name, reason } 目标 generation 已取得真实进度并完成画面槽位交换
player.quality.switch-failed { switchId, fromCode, toCode, name, reason, recovery, error } 切流失败;recovery='rolled-back'|'next-lower'|'none'
player.quality.switch-cancelled { switchId, fromCode, toCode, name, reason, cause, supersededBySwitchId? } 切流取消;cause='superseded'|'destroy'|'terminal'

reason'manual'|'downgrade'|'upgrade'|'decode-error'changed 是兼容的“目标改变”通知,switched 才是“目标流真实出帧”通知;新接入方应按同一 switchId 配对 switching 与三个终态事件。

事件名 Payload 说明
chat.message ChatMessage 新聊天消息
chat.pinned ChatMessage | null 置顶消息变更
chat.mute boolean | { muted: boolean } 全员禁言开关

ChatMessage.type'text' \| 'system' \| 'pinned' \| 'enter' \| 'welcome'welcome = 进场欢迎语,仅本端可见)。

IM 错误从全局 error 通道分流到 im.* 事件族,集成方可精细处理 IM 特定错误,与 im.reconnected 生命周期粒度对齐。

事件名 Payload 说明
auth.success { streamUrl: string, qualityList: QualityOption[] } auth API 成功,返回预设流地址与归一化清晰度列表
auth.kicked { code: number, reason: 'multi_device' | 'multi_account', tip: string } 单点登录冲突:当前账号已在其他设备/端登录(TIM KICKED_OUT 3002/3003)。触发后 SDK 会展示终态 Modal 并销毁实例,需刷新重新进入
im.ready undefined 腾讯 IM SDK 就绪
im.reconnected undefined IM 网络重连成功
im.user_banned { userId: string, reason?: string } 用户被禁言
im.user_kicked { userId: string, reason?: string } 用户被踢出群
im.error { code: number, message: string } IM 致命错误(如被踢下线)
im.net_error { code: number, message: string } IM 网络断开(与 im.reconnected 成对)
事件名 Payload 说明
ws.connected undefined WS 认证成功,连接就绪
ws.disconnected { reason?: string } 连接断开
ws.message WSMessage 原始 WS 帧(调试用)
ws.error { code: number, message: string } WS 错误
ws.auth_failed { code: number, message: string } 认证失败(重试耗尽)
ws.room_status { status: LiveStatus } 5000 ROOM_STATUS_NOTIFY 解析结果
ws.room_config Record<string, unknown> 5010 ROOM_CONFIG_NOTIFY 原始 payload(Phase 11 扇出 subfield)
ws.room_activity RoomActivityOp(判别联合:coupon_create / lucky_create / checkin_start / checkin_end / display / finish / result 6000/6020 房间营销统一事件族(签到/福袋/券);6020 仅本人 agentId 命中。详见 SPEC-ROOM-MARKETING.md §4
ws.server_time number(服务端毫秒) 服务端时钟锚定(roomMarketingStore.setServerTime
ws.reconnect { attempts: number } 重连 → roomMarketing entry resync

互动工具抽屉(内部 UI,非公开 API)FloatBar「更多」打开 FloatMoreDrawer 并调用 SDK 内部 _refreshInteractiveTools()RoomMarketingEntry.refreshInteractiveTools() → HTTP 79952 + store.reconcilePrime(不遍历 autoOpenIds)。prime/resync/refresh 共用 commitGeneration 门控(last-request-wins)。Store 派生 interactiveItemsrefreshing 仅 UI 指示。详情层 z-index:1200 覆盖抽屉。

| ws.commentary | Record<string, unknown> | 解说推送(NEEDS-SERVER-VERIFY)| | ws.goods_config | Record<string, unknown> | 商品配置更新推送(NEEDS-SERVER-VERIFY)|

事件名 Payload 说明
interact.like { count: number, delta: number } 点赞
interact.gift GiftPayload 礼物
interact.cart undefined 底部栏购物车点击
interact.share undefined 底部栏分享点击
interact.more { action?: string } 底部栏更多点击
事件名 Payload 说明
user.join UserProfile 用户进入直播间
room.viewerCount { count: number } 观看人数更新
事件名 Payload 说明
goods.show GoodsItem 展示商品卡片
goods.hide { goodsId: string } 隐藏商品卡片
goods.list GoodsCardItem[] HTTP 首屏全量列表就绪;项含可选 marketingActivities(营销活动摘要 actId/actType/actName,满减/满赠本地归一用)
goods.buy { numIid, scoreGoods? } 点购买/兑换按钮(列表卡 + 浮窗卡统一),host 接管下单
goods.card.click { numIid, name?, scoreGoods? } 商品卡片整体点击(列表卡 + 浮窗卡空白区,按钮已 stopPropagation
goods.promotion.click { numIid, scoreGoods? } 兑换券胶囊点击通知;autoOpenExchangeCoupon=false 时 host 可自开,true 时 SDK 已自开、host 不得重复打开
goods.cart.add { numIid, scoreGoods? } 列表卡「加入购物车」按钮点击(非积分商品、未售罄)
goods.cart.open undefined 商品橱窗标题栏「购物车」入口点击(区别于底栏 interact.cart
goods.order.open undefined 商品橱窗标题栏「订单」入口点击
goods.open / goods.close undefined 商品面板开 / 关
goods.marketing.open { numIid } 营销弹框打开
goods.marketing.show MarketingDetail 营销详情就绪(sections 可含 fullReduction 满减/满赠摘要,本地归一不发请求)
goods.marketing.error { numIid, message? } 营销拉取失败(可重试)
goods.marketing.close undefined 营销弹框关闭

⚠️ 破坏性:浮窗卡(HotSellCard)购买按钮由 interact.cart 统一为 goods.buy,旧宿主需迁移。

兑换券入口/弹框按 scoreGoodsInfo.useCouponType 分流(详见「数据模型」):1=商品券(分页「可用兑换券」+ 需要/已有)、2=普通券+万能券(内联「商品所需兑换券」+ 可显万能券)、0/空=不使用(无胶囊/弹窗)。商品不在列表且 scoreGoods=true 时程序式打开回退 type1。goods.promotion.click payload 与 autoOpenExchangeCoupon 语义不变。

事件名 Payload 说明
ws.room_activity RoomActivityOp(判别联合) 当前实现:6000/6020 房间营销统一事件族。券=福袋 type=1。详见 SPEC-ROOM-MARKETING.md §4

⚠️ 已废止事件(勿监听)activity.change / activity.result / activity.participate(旧 ActivityPanel 体系,已删组件);coupon.show / coupon.open / coupon.close(旧 CouponStack 徽标链路);ws.coupon_dispatch(旧 6000/6020 回落通道)。均已归 ws.room_activity + roomMarketingStore

左上角营销互动浮窗(image-text-float)

Section titled “左上角营销互动浮窗(image-text-float)”
事件名 Payload 说明
imagetext.change ImageTextChange LAPS check.in.* / lucky.packet.* 映射为浮窗数据变更;内部 ImageTextEntry 消费
imagetext.entry.click { type, current, stableKey } 用户点击浮窗图标;CHECKIN 路由到 checkin.show,其余面板留后续
imagetext.icon.close { stableKey, type } 用户关闭单个浮窗图标
imagetext.more.click undefined 用户打开「更多」抽屉
checkin.show / checkin.close { currentId } / undefined 签到面板打开 / 关闭
checkin.detail CheckInDetail 签到详情拉取成功
checkin.submit_result { success: boolean, message: string } 签到提交结果
checkin.kickout { banTips: string } 截止未签到且活动要求禁止观看
事件名 Payload 说明
features.update { partial: FeatureConfig, key?: FeatureKey } updateFeatures/setFeature 更新业务特性配置后 emit(setFeaturekey
more.quality undefined 「更多」面板清晰度入口点击(先于打开清晰度 ActionSheet)
more.fontSize undefined 「更多」面板字体大小入口点击(先于打开字号 ActionSheet)
more.cleanScreen undefined 「更多」面板清屏入口点击(先于进入清屏)
more.redPacket undefined 「更多」面板「我的红包」入口点击(先于 openRedPacket()
more.score undefined 「更多」面板「我的积分」入口点击(宿主导航积分中心)
more.coupon undefined 「更多」面板「我的券码」入口点击(宿主导航券码列表)
more.order undefined 「更多」面板「我的订单」入口点击(宿主导航订单列表)
more.feedback undefined 「更多」面板「意见/反馈」入口点击(先于转发 topbar.feedback
事件名 Payload 说明
watch.progress WatchProgressSnapshot 双门控观看快照;含 total/seconds/state/watchKind/byKind/sequence/reason
watch.state.changed { previous, current, snapshot } 观看状态变化
watch.report.calibration.started/ack/stale/failed/degraded WatchReportEventMeta ordinary/Range 校准与一致性结果
watch.report.queued/attempt/retry/ack/rejected/parked WatchReportEventMeta 可靠上报漏斗;只含脱敏元数据
watch.report.exit.queued/kickout.queued/finalize.error/final.abandoned WatchReportEventMeta 退出/踢出最终对账漏斗
duration.change { totalMs: number, seconds: number } 废弃watch.progress 的 5s 兼容映射
duration.report { seconds: number, success: boolean } 废弃;仅 ordinary 逻辑 ACK/终态失败
事件名 Payload 说明
app.module.init { moduleId: string } 应用模块初始化
app.module.destroy { moduleId: string } 应用模块销毁

Phase 11 入口模块事件(NEEDS-SERVER-VERIFY)

Section titled “Phase 11 入口模块事件(NEEDS-SERVER-VERIFY)”

以下事件 payload schema 由 5010 ROOM_CONFIG_NOTIFY subfield 推断,未经真实 LAPS 端点验证。

事件名 Payload 说明
lottery.start Record<string, unknown> 抽奖开始推送
lottery.open undefined 打开抽奖面板
lottery.close undefined 关闭抽奖面板
task.new Record<string, unknown> 新任务推送
task.open undefined 打开任务面板
task.close undefined 关闭任务面板
task.state.changed { state: TaskRewardEntryState } 任务、进度、领奖中和当前奖励的冻结快照
task.reward.present TaskRewardPresentation 新完成奖励串行呈现;宿主结束后调用 acknowledgeReward(key)
task.reward.closed { key, reason } 奖励关闭,reason 为 host_ack/destroy/skipped
task.receive.result { taskId, taskGenerationKey, success, ignoredAsStaleGeneration, error? } 普通任务手动领奖结果
goods.config Record<string, unknown> 商品列表配置推送
goods.open undefined 打开商品面板
goods.close undefined 关闭商品面板
commentary.push Record<string, unknown> 直播解说推送
commentary.open undefined 打开解说面板
commentary.close undefined 关闭解说面板

TaskRewardEntry 已从主包导出。宿主在 ready 后用 sdk.getAppModule<TaskRewardEntry>('taskReward') 获取,方法包括:refresh()getState()subscribe(listener)receiveOrdinaryTask({taskId,taskGenerationKey})acknowledgeReward(key)open()close()。状态使用 TaskRewardEntryState,任务视图为 TaskView | RangeTaskView 判别联合;返回值和事件 payload 均为深只读隔离快照。

  • 普通观看条件读取 watchCondition.watchHour/watchMinutes/checkInCount;互动条件读取 interactiveCondition.inviteType/inviteNum
  • 普通奖励读取 awardSetawardReceiveType,手动领奖仅允许 award.receiveMode==='manual'、已完成且仍在有效期内的 active generation。
  • Range 进度由 livingWatchSeconds + historyWatchSeconds + otherWatchSeconds 组成;未返回领取字段时 rewardState='unknown',不会默认成未领取;receiveOrdinaryTask() 不支持 Range。
  • progressByChannel.ordinary/range.effectiveSeconds 是跨设备服务端锚点叠加本机会话增量后的展示/阈值口径;不要用 getWatchProgress().seconds 替代服务端任务累计值。

完整接入示例见 GUIDE-APP-LAYER-MARKETING.md


interface ChatMessage {
id: string
userId: string
userName: string
userAvatar?: string
content: string
timestamp: number
type: 'text' | 'system' | 'pinned'
/** 来自 IM cloudCustomData.levelId,收端经 memberLevels 匹配 */
levelId?: string | number
levelName?: string
levelColor?: string
levelIcon?: string
}
interface GiftPayload {
giftId: string
giftName: string
giftIcon?: string
userId: string
userName: string
count: number
}
interface UserProfile {
userId: string
userName: string
avatar?: string
level?: number
}
interface GoodsItem {
id: string
name: string
price: number
image?: string
url?: string
}
// 商品关联营销活动摘要(goodsCard/get 商品项 marketingActivities 内联;仅摘要三字段,
// 无档位/赠品明细;满减/满赠本地归一为 fullReduction section,不发请求)
interface MarketingActivity {
actId: number // 活动 ID
actType: number // 活动类型码(0=满减, 1=满赠/赠送商品;抓包确认 2026-07-09)
actName: string // 摘要文案,如「满100减20」「满2件赠1」
}
// fullReduction section 数据(v2 摘要级);档位/赠品明细扩展位待后端补字段
interface FullReductionSectionData {
activities: MarketingActivity[]
}
// 商品列表项(goods.list payload 项)。仅列营销相关字段,其余基础字段同 GoodsItem。
interface GoodsCardItem {
numIid: string
scoreGoods?: boolean // 积分商品标识
/** 积分商品详情(scoreGoods=true 时存在) */
scoreGoodsInfo?: {
score?: number
price?: number
couponNum?: number
couponName?: string | null
couponId?: number | null
/** 列表态:type1=null(用 couponNum + 分页接口);type2 内联(实测项 {couponNum,couponId,couponName}) */
couponList?: Array<{
couponNum?: number
couponName?: string
couponId?: number
code?: string
consumeNum?: number
count?: number
name?: string // 对齐 live-app Coupon.name,couponName 缺失时作标题兜底
}> | null
exchangeNum?: number
limitNum?: number
/**
* 兑换券类型枚举(✅ 已确认 2026-07-14):
* 0/空 = 不使用(不渲染兑换券胶囊、不打开兑换券弹窗)
* 1 = 商品券(couponNum + 分页接口,列表态 couponList=null)
* 2 = 普通券+万能券(列表态内联 couponList)
*/
useCouponType?: number
}
marketingActivities?: MarketingActivity[]
/** 是否支持万能券(商品项顶层;仅 useCouponType=2 消费;缺失→false) */
supportUniversalCoupon?: boolean
}
// 兑换券(积分商品)section 数据;couponType 决定渲染分支
interface ExchangeCouponSectionData {
coupons: Array<{
couponNum?: number
couponName?: string
name?: string // 标题兜底(对齐 live-app Coupon.name)
couponId?: number
code?: string
consumeNum?: number // 计数回退链 couponNum → consumeNum → count
count?: number
}>
/** 兑换券类型:1=商品券(分页) / 2=普通券+万能券(内联);marketingApi 必填,驱动渲染分支 */
couponType?: 1 | 2
/** 商品所需兑换券总数(type1=scoreGoodsInfo.couponNum / type2=sum(couponList)),非券种数 */
count: number
/** 用户已持有数(mySelfCouponNum;仅 type1) */
mine?: number
/** 是否展示万能券提示(仅 type2) */
isSupportUniversalCoupon?: boolean
/** 分页(仅 type1) */
hasMore?: boolean
pageNo?: number
loadingMore?: boolean
loadMoreError?: string | null
}

通过 sdk.getModule<T>(id) 访问:

// Player 模块
const player = sdk.getModule<Player>('player')
player.play()
player.pause()
player.setVolume(0.8) // 0–1
player.attachElement(videoEl)
// IM 模块
const im = sdk.getModule<IM>('im')
await im.sendTextMessage('Hello!')
const history = await im.getHistoryMessages(20)
// Interact 模块
const interact = sdk.getModule<Interact>('interact')
await interact.sendLike(3)
// User 模块
const user = sdk.getModule<User>('user')
await user?.setProfile({ nickName: '昵称', avatar: 'https://...' }) // 部分字段可省略
await user?.refreshUserSig(newSig)
// 只读:user.userId · user.userName · user.avatar · user.levelId(来自 ve/token)

应用层入口模块(Phase 11,NEEDS-SERVER-VERIFY)

Section titled “应用层入口模块(Phase 11,NEEDS-SERVER-VERIFY)”
// 房间营销(签到 / 福袋 / 券)——命令式参与
await sdk.claimCoupon('packetId') // 实时券领取(上行 6010 + 等 6020 / 8s 乐观回退)
sdk.joinCheckIn('checkInId') // 签到(省略 id → 取当前 active)
await sdk.joinLuckyPacket('packetId') // 福袋参与(等开奖结果)
// 其它入口模块手动触发开/关
const lottery = sdk.getAppModule('lottery')
lottery?.open()

import { DanmuPlugin } from 'we-livesdk/plugins'
sdk.use(new DanmuPlugin({
speed: 8, // 滚动时长(秒)
fontSize: 14,
opacity: 0.85,
maxLines: 3,
}))
import { WatermarkPlugin } from 'we-livesdk/plugins'
sdk.use(new WatermarkPlugin({
text: 'Username',
position: 'top-right', // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
opacity: 0.4,
}))

清晰度自适应插件(opt-in,须显式注册)。开启后:起播按 defaultQuality 选档、弱网/解码丢帧自动降档、手动切档锁定;「更多」面板清晰度入口需此插件存在才显示(门控 hasMultiQuality && getPlugin('definition'))。

// ESM 主路径
import { LiveSDK, DefinitionPlugin } from 'we-livesdk'
const sdk = new LiveSDK({ activityId, token, merchantId })
sdk.use(new DefinitionPlugin({ defaultQuality: 'auto' }))
// 或子路径
import { DefinitionPlugin } from 'we-livesdk/plugins'
sdk.use(new DefinitionPlugin())
// UMD(裸 HTML)全局
sdk.use(new LiveSDK.DefinitionPlugin())
选项 默认 说明
defaultQuality 'preset' 'preset'(服务端预设源) | 'auto'(按设备分级) | 'highest' | 'lowest' | 具体 code
auto true 是否启用自动自适应(弱网/解码降级;false 仅手动切档)
allowUpgrade false 网络恢复后自动回升一档
只读 manualMode 手动锁定态(代理 playerStore.manualMode
方法 setManualMode(on) true 锁定禁用软性降级 / false 恢复自适应
manualOverrideMs @deprecated(手动锁定改为永久,不再用时间戳)
stallWindowMs 10000 单个冻结 episode 的最长观察时间(ms)
stallThreshold 3 可靠帧指标所需连续冻结窗;帧指标不可靠时使用 max(stallThreshold+1, 4)
cooldownMs 15000 自动降档软冷却(ms)
dropFrameSampleMs 5000 丢帧采样窗(ms);须连续 2 个有效窗命中
dropFrameRatio 0.1 每个有效窗的丢帧率阈值;窗内新增总帧不足 30 不计
decodeErrorCooldownMs 5000 解码错误硬路径冷却(ms)
decodeErrorThreshold 2 同档连续解码错误触发阈值

默认不变量:defaultQuality='preset'allowUpgrade=false;手动请求被 Player 接纳即设置 playerStore.manualMode=true,直到选择自动或调用 setManualMode(false)

运行约束:正常 101 进房会在 auth.success 前原子确定最终档位/URL,事件发出后只起播一次;108→101 会先退出 VOD,再真实恢复仍存在的手动档,档位已消失时使用服务端预设但保留锁定。运行时切流不立即替换当前源:目标流先在隐藏、静音的 standby 播放器中预加载,只有目标 generation 收到 loadstartplayingcurrentTime/totalVideoFrames 相对基线增长后,才交换画面并发 player.quality.switched。双槽位存在时,平台、音量、静音及微信桥接开声状态均不得触发 active 原地 switchStream();预加载期间开声只更新声音真值,不销毁 standby。交换时不修改旧 active 的 muted 状态,旧 active 停止后才把 Player 缓存的音量/静音应用到新 active。预加载失败/超时只释放 standby,当前源保持播放;非 superseded 取消还会把档位、URL 与 state sink 恢复到 confirmed 真值。公开 pause() 会同步暂停 active/standby 并挂起事务,play() 后恢复。候选和回滚中间错误不公开 player.error

健康判定约束:重复 waiting/stalled 可即时复采样,但不足 1 秒不得增加冻结窗口;观察到真实推进即结束本 episode。页面后台、暂停、VOD、初始化、切流事务、错误退避或直播终态期间不执行丢帧降档;手动锁定或最低档确认冻结时保持 stall-owner lease,并由 Player 执行同源恢复。


class AuthError extends Error {
code: number // HTTP 状态码或业务错误码
message: string
}

auth API 调用失败时抛出 AuthError,可在 mount() 的 catch 中捕获。


import { LiveSDK } from 'we-livesdk' // 主入口
const { LiveSDK: EmbeddedLiveSDK } = await import('we-livesdk/legacy') // 宿主安全 ESM;单文件、无 CSS 副作用
import { DefinitionPlugin } from 'we-livesdk' // 清晰度插件(主路径)
import { LiveRoom } from 'we-livesdk/components' // Svelte 组件
import { createChatStore } from 'we-livesdk/stores' // Store 工厂
import { DanmuPlugin } from 'we-livesdk/plugins' // 插件
import type { SDKConfig } from 'we-livesdk/types' // 仅类型
import 'we-livesdk/styles' // Tailwind CSS
import 'we-livesdk/embed.css' // 完整嵌入样式:Tailwind + Svelte/TCPlayer,命名空间隔离

we-livesdk/embed.css 关闭 preflight,包含 Tailwind utilities 与构建期提取的 Svelte scoped/TCPlayer 组件样式;所有规则统一限制在 .livesdk-container 下。pnpm build / pnpm package 会生成标准 Svelte package、dist/embed.cssdist-umd/livesdk.legacy.js 并执行 publint;clean checkout 不依赖残留构建产物。pnpm build:umd 额外生成 UMD/IIFE/ESM 演示产物,并同步重建 legacy 入口。


import { LiveSDK, SDK_EVENTS } from 'we-livesdk'
import { DanmuPlugin } from 'we-livesdk/plugins'
import 'we-livesdk/styles'
const sdk = new LiveSDK({
activityId: 'room-001',
token: 'your-jwt-token',
merchantId: 'your-merchant-id',
locale: 'zh-CN',
tim: {
sdkAppId: 1400000000,
userId: 'viewer-123',
userSig: await fetchUserSig('viewer-123'),
groupId: 'live-room-001',
},
ws: {
url: 'wss://your-laps-host/ws',
},
layout: {
orientation: 'auto', // 读 API body.extendSet.screenType
},
options: {
logLevel: 'info',
theme: {
primaryColor: '#ff6b35',
radius: '8px',
},
},
})
sdk.use(new DanmuPlugin({ speed: 8 }))
sdk.on(SDK_EVENTS.READY, () => {
console.log('SDK ready')
})
sdk.on(SDK_EVENTS.LIVE_ORIENTATION, (orientation) => {
console.log('布局方向:', orientation) // 'portrait' | 'landscape'
})
sdk.on(SDK_EVENTS.CHAT_MESSAGE, (msg) => {
console.log(`${msg.userName}: ${msg.content}`)
})
sdk.on(SDK_EVENTS.DURATION_CHANGE, ({ seconds }) => {
console.log('已观看', seconds, '')
})
sdk.on(SDK_EVENTS.PLAYER_ERROR, ({ code, message }) => {
console.error(`播放错误 ${code}: ${message}`)
})
// WS 连接状态
sdk.on(SDK_EVENTS.WS_CONNECTED, () => {
console.log('WebSocket 已连接')
})
sdk.on(SDK_EVENTS.WS_ROOM_STATUS, ({ status }) => {
console.log('直播状态(WS推送):', status)
})
await sdk.mount('#live-container')
// 清理
window.addEventListener('beforeunload', () => sdk.destroy())

telemetry 默认关闭,且与 options.logLevel 独立。公开字段为 enabled/profile/endpoint/transport/identityMode/experiments/diagnostic/sampleRate/batchSize/flushIntervalMs/maxQueue/maxBytes/appContextendpointtransport 互斥:开发态冲突抛配置错误,生产态关闭遥测并告警,不中断 SDK。ctx.userKey 始终上报去首尾空白后的明文用户标识,ctx.deviceKey 继续使用 SHA-256 截断哈希;空用户不产生 userKeyidentityMode 与旧 appContext.plaintextIdentity 仅为兼容保留字段,不再改变身份输出。生产采样率无有效 diagnostic 时最高 0.1;只有 diagnostic.userKey 精确匹配明文 ctx.userKeyexpiresAt 在未来 30 分钟内才提升到 1。

根入口与 we-livesdk/types 均导出 TelemetryConfig/TelemetryTransport/TelemetryBatchV3/TelemetryContext/TelemetryProfile/TransportResult/SendMeta/ExitMeta 类型,第三方 Transport 无需引用内部路径。

批次为 {v:3,bid,sid,bseq,t0,ctx,events,sidecars?,stats?},事件元组为 [code,seq,dt,...numberPayload]。宿主 appContext 的页面/应用/构建字段会进入受控上下文,用户标识以明文 userKey 上报,设备标识保持哈希。内置 HTTP 用 text/plain;charset=UTF-8,单批 ≤48KiB、队列 ≤100 条/64KiB。

完整解析契约见 TELEMETRY-V3-REFERENCE.md:14/14 个事件均登记精确 payload 位置、类型、单位、枚举/位图,顶层 batch、ctx/stats/sidecars 也逐字段登记。真源 packages/live-observability-contract/manifest.json 同时生成 JSON Schema、本地枚举和参考表;严格解析器会拒绝未知 code、错误 arity、未知枚举/位图及未登记字段。服务端导出直接 batch、batch 数组或逐行 JSON 后,可执行 node packages/live-observability-contract/scripts/decode.mjs exported-log.json。本契约只验收客户端请求体完整可解析,不要求证明服务端已经接收或落库。