@tiktool/live 2.6.7 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -323
- package/dist/index.d.mts +83 -707
- package/dist/index.d.ts +83 -707
- package/dist/index.js +429 -998
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +428 -992
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -17
package/dist/index.d.mts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import * as http from 'http';
|
|
3
2
|
|
|
4
3
|
interface TikTokUser {
|
|
5
4
|
id: string;
|
|
6
5
|
nickname: string;
|
|
7
6
|
uniqueId: string;
|
|
8
7
|
profilePicture?: string;
|
|
9
|
-
|
|
8
|
+
/** High-resolution avatar URL from protobuf field 11 (avatarLarge ~720x720) */
|
|
9
|
+
avatarLargeUrl?: string;
|
|
10
10
|
badges?: string[];
|
|
11
|
-
/** User's gifter/spender level (1-50). Higher = more coins spent platform-wide. */
|
|
12
|
-
payGrade?: number;
|
|
13
11
|
}
|
|
14
12
|
interface BaseEvent {
|
|
15
13
|
type: string;
|
|
@@ -20,6 +18,11 @@ interface ChatEvent extends BaseEvent {
|
|
|
20
18
|
type: 'chat';
|
|
21
19
|
user: TikTokUser;
|
|
22
20
|
comment: string;
|
|
21
|
+
emotes?: Array<{
|
|
22
|
+
emoteId: string;
|
|
23
|
+
imageUrl: string;
|
|
24
|
+
placeInComment: number;
|
|
25
|
+
}>;
|
|
23
26
|
}
|
|
24
27
|
interface MemberEvent extends BaseEvent {
|
|
25
28
|
type: 'member';
|
|
@@ -43,7 +46,6 @@ interface GiftEvent extends BaseEvent {
|
|
|
43
46
|
combo: boolean;
|
|
44
47
|
giftType: number;
|
|
45
48
|
groupId: string;
|
|
46
|
-
giftPictureUrl?: string;
|
|
47
49
|
}
|
|
48
50
|
interface SocialEvent extends BaseEvent {
|
|
49
51
|
type: 'social';
|
|
@@ -61,9 +63,9 @@ interface BattleTeamUser {
|
|
|
61
63
|
}
|
|
62
64
|
interface BattleTeam {
|
|
63
65
|
hostUserId: string;
|
|
66
|
+
hostUser?: TikTokUser;
|
|
64
67
|
score: number;
|
|
65
68
|
users: BattleTeamUser[];
|
|
66
|
-
hostUser?: TikTokUser;
|
|
67
69
|
}
|
|
68
70
|
interface BattleEvent extends BaseEvent {
|
|
69
71
|
type: 'battle';
|
|
@@ -71,45 +73,20 @@ interface BattleEvent extends BaseEvent {
|
|
|
71
73
|
status: number;
|
|
72
74
|
battleDuration: number;
|
|
73
75
|
teams: BattleTeam[];
|
|
74
|
-
battleSettings?: {
|
|
75
|
-
startTimeMs?: number;
|
|
76
|
-
duration?: number;
|
|
77
|
-
endTimeMs?: number;
|
|
78
|
-
};
|
|
79
76
|
}
|
|
80
77
|
interface BattleArmiesEvent extends BaseEvent {
|
|
81
78
|
type: 'battleArmies';
|
|
82
79
|
battleId: string;
|
|
83
80
|
status: number;
|
|
84
81
|
teams: BattleTeam[];
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
interface BattleTaskEvent extends BaseEvent {
|
|
94
|
-
type: 'battleTask';
|
|
95
|
-
taskAction: number;
|
|
96
|
-
battleRefId: string;
|
|
97
|
-
missionType: string;
|
|
98
|
-
multiplier: number;
|
|
99
|
-
missionDuration: number;
|
|
100
|
-
missionTarget: number;
|
|
101
|
-
remainingSeconds: number;
|
|
102
|
-
endTimestampS: number;
|
|
103
|
-
timerType: number;
|
|
104
|
-
}
|
|
105
|
-
interface BarrageEvent extends BaseEvent {
|
|
106
|
-
type: 'barrage';
|
|
107
|
-
msgType: number;
|
|
108
|
-
subType: number;
|
|
109
|
-
displayType: number;
|
|
110
|
-
duration: number;
|
|
111
|
-
defaultPattern: string;
|
|
112
|
-
content: string;
|
|
82
|
+
/** Battle start timestamp in milliseconds (from f18.f2) */
|
|
83
|
+
battleStartMs: number;
|
|
84
|
+
/** Total battle duration in seconds (from f18.f3, typically 300) */
|
|
85
|
+
battleDurationSeconds: number;
|
|
86
|
+
/** Seconds remaining in the battle, computed as max(0, duration - elapsed). Note: uses VPS clock, may be off by up to 30s */
|
|
87
|
+
timeLeftSeconds: number;
|
|
88
|
+
/** Battle end timestamp in milliseconds (battleStartMs + duration*1000). Use for clock-independent timer: Math.max(0, (endTimeMs - Date.now()) / 1000) */
|
|
89
|
+
endTimeMs: number;
|
|
113
90
|
}
|
|
114
91
|
interface SubscribeEvent extends BaseEvent {
|
|
115
92
|
type: 'subscribe';
|
|
@@ -121,7 +98,6 @@ interface EmoteChatEvent extends BaseEvent {
|
|
|
121
98
|
user: TikTokUser;
|
|
122
99
|
emoteId: string;
|
|
123
100
|
emoteUrl: string;
|
|
124
|
-
emoteName?: string;
|
|
125
101
|
}
|
|
126
102
|
interface EnvelopeEvent extends BaseEvent {
|
|
127
103
|
type: 'envelope';
|
|
@@ -164,7 +140,7 @@ interface UnknownEvent extends BaseEvent {
|
|
|
164
140
|
type: 'unknown';
|
|
165
141
|
method: string;
|
|
166
142
|
}
|
|
167
|
-
type LiveEvent = ChatEvent | MemberEvent | LikeEvent | GiftEvent | SocialEvent | RoomUserSeqEvent | BattleEvent | BattleArmiesEvent |
|
|
143
|
+
type LiveEvent = ChatEvent | MemberEvent | LikeEvent | GiftEvent | SocialEvent | RoomUserSeqEvent | BattleEvent | BattleArmiesEvent | SubscribeEvent | EmoteChatEvent | EnvelopeEvent | QuestionEvent | ControlEvent | RoomEvent | LiveIntroEvent | RankUpdateEvent | LinkMicEvent | UnknownEvent;
|
|
168
144
|
interface TikTokLiveEvents {
|
|
169
145
|
connected: () => void;
|
|
170
146
|
disconnected: (code: number, reason: string) => void;
|
|
@@ -178,8 +154,6 @@ interface TikTokLiveEvents {
|
|
|
178
154
|
roomUserSeq: (event: RoomUserSeqEvent) => void;
|
|
179
155
|
battle: (event: BattleEvent) => void;
|
|
180
156
|
battleArmies: (event: BattleArmiesEvent) => void;
|
|
181
|
-
battleTask: (event: BattleTaskEvent) => void;
|
|
182
|
-
barrage: (event: BarrageEvent) => void;
|
|
183
157
|
subscribe: (event: SubscribeEvent) => void;
|
|
184
158
|
emoteChat: (event: EmoteChatEvent) => void;
|
|
185
159
|
envelope: (event: EnvelopeEvent) => void;
|
|
@@ -197,7 +171,30 @@ interface RoomInfo {
|
|
|
197
171
|
wsHost: string;
|
|
198
172
|
clusterRegion: string;
|
|
199
173
|
connectedAt: string;
|
|
200
|
-
|
|
174
|
+
}
|
|
175
|
+
/** Quality tiers available for live stream video */
|
|
176
|
+
type StreamQuality = 'FULL_HD1' | 'HD1' | 'SD1' | 'SD2' | 'origin' | string;
|
|
177
|
+
/** Stream URLs for a specific quality level */
|
|
178
|
+
interface StreamUrls {
|
|
179
|
+
/** FLV pull URL for this quality */
|
|
180
|
+
flv?: string;
|
|
181
|
+
/** HLS (m3u8) pull URL for this quality */
|
|
182
|
+
hls?: string;
|
|
183
|
+
}
|
|
184
|
+
/** Full stream info returned by getStreamUrl() */
|
|
185
|
+
interface StreamInfo {
|
|
186
|
+
/** Room ID of the live stream */
|
|
187
|
+
roomId: string;
|
|
188
|
+
/** Whether the user is currently live */
|
|
189
|
+
alive: boolean;
|
|
190
|
+
/** Stream URLs keyed by quality (FULL_HD1, HD1, SD1, SD2) */
|
|
191
|
+
streamUrls: Record<StreamQuality, StreamUrls>;
|
|
192
|
+
/** Recommended default quality */
|
|
193
|
+
defaultQuality: StreamQuality;
|
|
194
|
+
/** Best available FLV URL (convenience shortcut) */
|
|
195
|
+
flvPullUrl?: string;
|
|
196
|
+
/** Best available HLS URL (convenience shortcut) */
|
|
197
|
+
hlsPullUrl?: string;
|
|
201
198
|
}
|
|
202
199
|
interface TikTokLiveOptions {
|
|
203
200
|
uniqueId: string;
|
|
@@ -207,236 +204,10 @@ interface TikTokLiveOptions {
|
|
|
207
204
|
maxReconnectAttempts?: number;
|
|
208
205
|
heartbeatInterval?: number;
|
|
209
206
|
debug?: boolean;
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* TikTok browser session ID cookie for authenticated connections.
|
|
213
|
-
* Enables ranklist API access and authenticated WebSocket features.
|
|
214
|
-
* Obtain from the user's tiktok.com browser cookies.
|
|
215
|
-
*/
|
|
216
|
-
sessionId?: string;
|
|
217
|
-
/**
|
|
218
|
-
* TikTok target IDC region (e.g. 'useast5').
|
|
219
|
-
* Required when sessionId is provided — must match the account's region.
|
|
220
|
-
*/
|
|
221
|
-
ttTargetIdc?: string;
|
|
222
|
-
/**
|
|
223
|
-
* HTTP agent for routing connections through a proxy.
|
|
224
|
-
* Pass an HttpsProxyAgent (or any http.Agent) to route both the
|
|
225
|
-
* initial HTTP request and WebSocket connection through a proxy.
|
|
226
|
-
*
|
|
227
|
-
* Example:
|
|
228
|
-
* import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
229
|
-
* agent: new HttpsProxyAgent('http://user:pass@host:port')
|
|
230
|
-
*/
|
|
231
|
-
agent?: http.Agent;
|
|
232
|
-
/**
|
|
233
|
-
* If you already know the room ID (e.g. from a leaderboard API),
|
|
234
|
-
* pass it here to skip the HTML page scrape entirely.
|
|
235
|
-
* This avoids geo-restriction issues and speeds up connection.
|
|
236
|
-
*/
|
|
207
|
+
/** Pre-resolved room ID — skips direct TikTok page fetch when provided with sessionId */
|
|
237
208
|
roomId?: string;
|
|
238
|
-
/**
|
|
239
|
-
|
|
240
|
-
* the library skips the HTTP fetch to tiktok.com entirely.
|
|
241
|
-
* Fetch once and share across multiple TikTokLive instances.
|
|
242
|
-
*/
|
|
243
|
-
ttwid?: string;
|
|
244
|
-
}
|
|
245
|
-
interface RanklistUser {
|
|
246
|
-
/** TikTok user ID */
|
|
247
|
-
id_str: string;
|
|
248
|
-
/** Display name */
|
|
249
|
-
nickname: string;
|
|
250
|
-
/** Username (@handle) */
|
|
251
|
-
display_id?: string;
|
|
252
|
-
unique_id?: string;
|
|
253
|
-
/** Avatar thumbnail URLs */
|
|
254
|
-
avatar_thumb?: {
|
|
255
|
-
url_list: string[];
|
|
256
|
-
};
|
|
257
|
-
/** Full avatar URLs */
|
|
258
|
-
avatar_medium?: {
|
|
259
|
-
url_list: string[];
|
|
260
|
-
};
|
|
261
|
-
/** Follower info */
|
|
262
|
-
follow_info?: {
|
|
263
|
-
follow_status: number;
|
|
264
|
-
follower_count: number;
|
|
265
|
-
following_count: number;
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Entry from online_audience endpoint (in-room top gifters).
|
|
270
|
-
* Has explicit `rank` (1-based) and `score` fields.
|
|
271
|
-
*/
|
|
272
|
-
interface OnlineAudienceEntry {
|
|
273
|
-
rank: number;
|
|
274
|
-
score: number;
|
|
275
|
-
user: RanklistUser;
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Entry from anchor/rank_list endpoint (leaderboard/gifter rankings).
|
|
279
|
-
* Position is the array index. Uses `value` (not `score`).
|
|
280
|
-
*/
|
|
281
|
-
interface AnchorRankListEntry {
|
|
282
|
-
/** Gift value (diamonds/coins) */
|
|
283
|
-
value: number;
|
|
284
|
-
/** Rank category type (e.g. 3 = gifter) */
|
|
285
|
-
rank_type: number;
|
|
286
|
-
/** Time period type (e.g. 1 = hourly) */
|
|
287
|
-
rank_time_type: number;
|
|
288
|
-
/** Auto-thanks message configured by anchor */
|
|
289
|
-
auto_thanks_message?: string;
|
|
290
|
-
/** Schema URL for navigation */
|
|
291
|
-
schema_url?: string;
|
|
292
|
-
/** Highlight DM share status */
|
|
293
|
-
highlight_dm_share_status?: number;
|
|
294
|
-
/** Ranked user data */
|
|
295
|
-
user: RanklistUser;
|
|
296
|
-
}
|
|
297
|
-
interface RanklistSelfInfo {
|
|
298
|
-
rank: number;
|
|
299
|
-
score: number;
|
|
300
|
-
gap_description?: string;
|
|
301
|
-
}
|
|
302
|
-
/** Response from "online_audience" sub-endpoint — in-room top gifters */
|
|
303
|
-
interface OnlineAudienceResponse {
|
|
304
|
-
status_code: number;
|
|
305
|
-
data: {
|
|
306
|
-
ranks: OnlineAudienceEntry[];
|
|
307
|
-
self_info?: RanklistSelfInfo;
|
|
308
|
-
currency?: string;
|
|
309
|
-
total?: number;
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
/** Response from "anchor_rank_list" sub-endpoint — gifter leaderboard */
|
|
313
|
-
interface AnchorRankListResponse {
|
|
314
|
-
status_code: number;
|
|
315
|
-
data: {
|
|
316
|
-
rank_list: AnchorRankListEntry[];
|
|
317
|
-
/** Room where ranks were accumulated */
|
|
318
|
-
latest_room_id_str?: string;
|
|
319
|
-
/** Rank period start (unix timestamp) */
|
|
320
|
-
rank_time_begin?: number;
|
|
321
|
-
/** Rank period end (unix timestamp) */
|
|
322
|
-
rank_time_end?: number;
|
|
323
|
-
/** Whether to show rank summary */
|
|
324
|
-
show_rank_summary?: boolean;
|
|
325
|
-
/** Default rank type for this anchor */
|
|
326
|
-
default_rank_type?: number;
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
/** Entrance tab info from "entrance" sub-endpoint */
|
|
330
|
-
interface EntranceTab {
|
|
331
|
-
title: string;
|
|
332
|
-
rank_type: number;
|
|
333
|
-
list_lynx_type?: number;
|
|
334
|
-
}
|
|
335
|
-
/** Entrance config for a single rank_type */
|
|
336
|
-
interface EntranceInfo {
|
|
337
|
-
rank_type: number;
|
|
338
|
-
/** Whether the anchor appears in this ranking */
|
|
339
|
-
owner_on_rank: boolean;
|
|
340
|
-
/** Anchor's position in this ranking (0-based) */
|
|
341
|
-
owner_rank_idx?: number;
|
|
342
|
-
/** Current score in this ranking */
|
|
343
|
-
current_score?: number;
|
|
344
|
-
/** Countdown in seconds until ranking resets */
|
|
345
|
-
countdown?: number;
|
|
346
|
-
/** Window size in seconds (86400 = daily) */
|
|
347
|
-
window_size?: number;
|
|
348
|
-
/** Unix timestamp when ranking resets */
|
|
349
|
-
reset_time?: number;
|
|
350
|
-
/** Related tab to show when clicking this entrance */
|
|
351
|
-
related_tab_rank_type?: number;
|
|
352
|
-
/** Gap description with points needed to reach a rank */
|
|
353
|
-
affiliated_content?: {
|
|
354
|
-
gap_desc?: {
|
|
355
|
-
default_pattern: string;
|
|
356
|
-
pieces?: Array<{
|
|
357
|
-
string_value: string;
|
|
358
|
-
type: number;
|
|
359
|
-
}>;
|
|
360
|
-
};
|
|
361
|
-
};
|
|
362
|
-
/** Class/League info */
|
|
363
|
-
class_info?: {
|
|
364
|
-
class_type: number;
|
|
365
|
-
star_count: number;
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
interface EntranceResponse {
|
|
369
|
-
status_code: number;
|
|
370
|
-
data: Array<{
|
|
371
|
-
group_type?: number;
|
|
372
|
-
Priority?: number;
|
|
373
|
-
data?: {
|
|
374
|
-
tabs?: EntranceTab[];
|
|
375
|
-
entrances?: EntranceInfo[];
|
|
376
|
-
};
|
|
377
|
-
}>;
|
|
378
|
-
}
|
|
379
|
-
type RanklistResponse = OnlineAudienceResponse | AnchorRankListResponse | EntranceResponse;
|
|
380
|
-
/** Streamer info in a feed room entry */
|
|
381
|
-
interface FeedRoomOwner {
|
|
382
|
-
/** TikTok user ID */
|
|
383
|
-
id_str: string;
|
|
384
|
-
/** Username (@handle) */
|
|
385
|
-
display_id: string;
|
|
386
|
-
/** Display name */
|
|
387
|
-
nickname: string;
|
|
388
|
-
/** Avatar thumbnail URLs */
|
|
389
|
-
avatar_thumb?: {
|
|
390
|
-
url_list: string[];
|
|
391
|
-
};
|
|
392
|
-
/** Avatar large URLs */
|
|
393
|
-
avatar_large?: {
|
|
394
|
-
url_list: string[];
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
/** A single live room entry from the feed */
|
|
398
|
-
interface FeedRoom {
|
|
399
|
-
/** Room ID */
|
|
400
|
-
id_str: string;
|
|
401
|
-
/** Stream title */
|
|
402
|
-
title: string;
|
|
403
|
-
/** Current viewer count */
|
|
404
|
-
user_count: number;
|
|
405
|
-
/** Stream cover image URL */
|
|
406
|
-
cover?: {
|
|
407
|
-
url_list: string[];
|
|
408
|
-
};
|
|
409
|
-
/** Streamer info */
|
|
410
|
-
owner: FeedRoomOwner;
|
|
411
|
-
/** Stream status (2 = live) */
|
|
412
|
-
status: number;
|
|
413
|
-
/** Stream start time (unix seconds) */
|
|
414
|
-
create_time?: number;
|
|
415
|
-
/** Like count */
|
|
416
|
-
like_count?: number;
|
|
417
|
-
/** Hashtag IDs */
|
|
418
|
-
hashtag_ids?: string[];
|
|
419
|
-
}
|
|
420
|
-
/** Signed-URL response from GET/POST /webcast/feed */
|
|
421
|
-
interface FeedSignedResponse {
|
|
422
|
-
/** Always 0 on success */
|
|
423
|
-
status_code: number;
|
|
424
|
-
/** The signed TikTok URL to fetch */
|
|
425
|
-
signed_url: string;
|
|
426
|
-
/** Required headers */
|
|
427
|
-
headers: Record<string, string>;
|
|
428
|
-
/** Cookies to include (ttwid, sessionid, etc.) */
|
|
429
|
-
cookies?: string;
|
|
430
|
-
/** Region used */
|
|
431
|
-
region: string;
|
|
432
|
-
/** Channel ID used */
|
|
433
|
-
channel_id: string;
|
|
434
|
-
/** Remaining daily feed calls */
|
|
435
|
-
feed_remaining: number;
|
|
436
|
-
/** Daily feed call limit */
|
|
437
|
-
feed_limit: number;
|
|
438
|
-
/** Human-readable instructions */
|
|
439
|
-
note: string;
|
|
209
|
+
/** Pre-resolved ttwid session cookie — skips direct TikTok page fetch when provided with roomId */
|
|
210
|
+
sessionId?: string;
|
|
440
211
|
}
|
|
441
212
|
|
|
442
213
|
declare class TikTokLive extends EventEmitter {
|
|
@@ -445,9 +216,12 @@ declare class TikTokLive extends EventEmitter {
|
|
|
445
216
|
private reconnectAttempts;
|
|
446
217
|
private intentionalClose;
|
|
447
218
|
private _connected;
|
|
219
|
+
private _destroyed;
|
|
448
220
|
private _eventCount;
|
|
449
221
|
private _roomId;
|
|
450
|
-
private
|
|
222
|
+
private static readonly MAX_BATTLE_HOSTS;
|
|
223
|
+
private _battleHosts;
|
|
224
|
+
private _pendingHostResolves;
|
|
451
225
|
private readonly uniqueId;
|
|
452
226
|
private readonly signServerUrl;
|
|
453
227
|
private readonly apiKey;
|
|
@@ -455,453 +229,55 @@ declare class TikTokLive extends EventEmitter {
|
|
|
455
229
|
private readonly maxReconnectAttempts;
|
|
456
230
|
private readonly heartbeatInterval;
|
|
457
231
|
private readonly debug;
|
|
458
|
-
private
|
|
459
|
-
private
|
|
460
|
-
private readonly proxyAgent?;
|
|
461
|
-
private readonly _presetRoomId?;
|
|
462
|
-
private readonly _presetTtwid?;
|
|
232
|
+
private readonly _presetRoomId;
|
|
233
|
+
private readonly _presetSessionId;
|
|
463
234
|
constructor(options: TikTokLiveOptions);
|
|
464
235
|
connect(): Promise<void>;
|
|
465
236
|
disconnect(): void;
|
|
237
|
+
/**
|
|
238
|
+
* Fully destroy the client, releasing all resources and listeners.
|
|
239
|
+
* After calling destroy(), the instance cannot be reused — create a new one.
|
|
240
|
+
*/
|
|
241
|
+
destroy(): void;
|
|
466
242
|
get connected(): boolean;
|
|
243
|
+
get destroyed(): boolean;
|
|
467
244
|
get eventCount(): number;
|
|
468
245
|
get roomId(): string;
|
|
469
|
-
/** Get the stored session ID (if any) */
|
|
470
|
-
get sessionId(): string | undefined;
|
|
471
|
-
/** Update the session ID at runtime (e.g. after TikTok login) */
|
|
472
|
-
setSession(sessionId: string, ttTargetIdc?: string): void;
|
|
473
246
|
/**
|
|
474
|
-
*
|
|
475
|
-
* Returns
|
|
247
|
+
* Get live stream video URLs for a TikTok user.
|
|
248
|
+
* Returns FLV & HLS URLs segmented by quality (FULL_HD1, HD1, SD1, SD2).
|
|
249
|
+
* This is a standalone method — no WebSocket connection required.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* const stream = await TikTokLive.getStreamUrl({
|
|
254
|
+
* uniqueId: 'username',
|
|
255
|
+
* apiKey: 'your-api-key',
|
|
256
|
+
* });
|
|
257
|
+
* if (stream.alive) {
|
|
258
|
+
* console.log(stream.streamUrls.HD1?.hls); // HLS URL for HD quality
|
|
259
|
+
* console.log(stream.flvPullUrl); // Best FLV URL
|
|
260
|
+
* }
|
|
261
|
+
* ```
|
|
476
262
|
*/
|
|
477
|
-
|
|
263
|
+
static getStreamUrl(options: {
|
|
264
|
+
uniqueId: string;
|
|
265
|
+
apiKey: string;
|
|
266
|
+
signServerUrl?: string;
|
|
267
|
+
quality?: string;
|
|
268
|
+
}): Promise<StreamInfo>;
|
|
478
269
|
on<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
|
|
479
270
|
once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
|
|
480
271
|
off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
|
|
481
272
|
emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean;
|
|
482
273
|
private handleFrame;
|
|
483
|
-
private startHeartbeat;
|
|
484
|
-
private stopHeartbeat;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* TikTokCaptions — Real-time speech-to-text transcription and translation for TikTok LIVE streams.
|
|
489
|
-
*
|
|
490
|
-
* AI-powered audio transcription with speaker diarization, multi-language auto-detection,
|
|
491
|
-
* real-time translation, and sub-second latency. Connects via WebSocket to the TikTool
|
|
492
|
-
* captions relay for continuous streaming transcription.
|
|
493
|
-
*
|
|
494
|
-
* @example
|
|
495
|
-
* ```ts
|
|
496
|
-
* import { TikTokCaptions } from '@tiktool/live';
|
|
497
|
-
*
|
|
498
|
-
* const captions = new TikTokCaptions({
|
|
499
|
-
* uniqueId: 'username',
|
|
500
|
-
* apiKey: 'your-api-key',
|
|
501
|
-
* language: 'en', // translate to English
|
|
502
|
-
* });
|
|
503
|
-
*
|
|
504
|
-
* captions.on('caption', (data) => {
|
|
505
|
-
* console.log(`[${data.language}] ${data.text}`);
|
|
506
|
-
* });
|
|
507
|
-
*
|
|
508
|
-
* captions.on('translation', (data) => {
|
|
509
|
-
* console.log(`[translated] ${data.text}`);
|
|
510
|
-
* });
|
|
511
|
-
*
|
|
512
|
-
* await captions.start();
|
|
513
|
-
* ```
|
|
514
|
-
*/
|
|
515
|
-
|
|
516
|
-
interface TikTokCaptionsOptions {
|
|
517
|
-
/** TikTok username to transcribe (without @) */
|
|
518
|
-
uniqueId: string;
|
|
519
|
-
/** API key for authentication */
|
|
520
|
-
apiKey: string;
|
|
521
|
-
/** Source language hint (e.g. 'en', 'ja'). Leave empty for auto-detection. */
|
|
522
|
-
language?: string;
|
|
523
|
-
/** Target language for real-time translation (e.g. 'en', 'es', 'fr'). One language per session. */
|
|
524
|
-
translate?: string;
|
|
525
|
-
/** Enable speaker diarization to identify individual speakers (default: true) */
|
|
526
|
-
diarization?: boolean;
|
|
527
|
-
/** Max session duration in minutes before auto-disconnect (default: 60, max: 300) */
|
|
528
|
-
maxDurationMinutes?: number;
|
|
529
|
-
/** Custom server URL (default: wss://api.tik.tools) */
|
|
530
|
-
signServerUrl?: string;
|
|
531
|
-
/** Enable debug logging */
|
|
532
|
-
debug?: boolean;
|
|
533
|
-
/** Auto-reconnect on disconnect */
|
|
534
|
-
autoReconnect?: boolean;
|
|
535
|
-
/** Max reconnect attempts (default: 5) */
|
|
536
|
-
maxReconnectAttempts?: number;
|
|
537
|
-
}
|
|
538
|
-
interface CaptionData {
|
|
539
|
-
/** Transcribed text */
|
|
540
|
-
text: string;
|
|
541
|
-
/** Detected source language */
|
|
542
|
-
language: string;
|
|
543
|
-
/** Whether this is the final version of this segment */
|
|
544
|
-
isFinal: boolean;
|
|
545
|
-
/** Confidence score (0-1) */
|
|
546
|
-
confidence: number;
|
|
547
|
-
/** Speaker identifier (if available) */
|
|
548
|
-
speaker?: string;
|
|
549
|
-
/** Start time in milliseconds */
|
|
550
|
-
startMs?: number;
|
|
551
|
-
/** End time in milliseconds */
|
|
552
|
-
endMs?: number;
|
|
553
|
-
}
|
|
554
|
-
interface TranslationData {
|
|
555
|
-
/** Translated text */
|
|
556
|
-
text: string;
|
|
557
|
-
/** Target language */
|
|
558
|
-
language: string;
|
|
559
|
-
/** Whether this is the final version */
|
|
560
|
-
isFinal: boolean;
|
|
561
|
-
/** Confidence score (0-1) */
|
|
562
|
-
confidence: number;
|
|
563
|
-
/** Speaker identifier (if available) */
|
|
564
|
-
speaker?: string;
|
|
565
|
-
}
|
|
566
|
-
interface CaptionCredits {
|
|
567
|
-
/** Credits remaining */
|
|
568
|
-
remaining: number;
|
|
569
|
-
/** Total credits purchased */
|
|
570
|
-
total: number;
|
|
571
|
-
/** Credits used in this session */
|
|
572
|
-
used: number;
|
|
573
|
-
/** Whether credits are low */
|
|
574
|
-
warning: boolean;
|
|
575
|
-
}
|
|
576
|
-
interface CaptionStatus {
|
|
577
|
-
/** Current status */
|
|
578
|
-
status: 'connecting' | 'waiting' | 'live' | 'transcribing' | 'ended' | 'switching_language' | 'language_switched' | 'stream_ended';
|
|
579
|
-
/** TikTok username */
|
|
580
|
-
uniqueId?: string;
|
|
581
|
-
/** Room ID (once resolved) */
|
|
582
|
-
roomId?: string;
|
|
583
|
-
/** Language (for language switch events) */
|
|
584
|
-
language?: string;
|
|
585
|
-
/** Status message */
|
|
586
|
-
message?: string;
|
|
587
|
-
}
|
|
588
|
-
interface CaptionError {
|
|
589
|
-
/** Error code */
|
|
590
|
-
code: string;
|
|
591
|
-
/** Human-readable error message */
|
|
592
|
-
message: string;
|
|
593
|
-
}
|
|
594
|
-
interface TikTokCaptionsEvents {
|
|
595
|
-
/** Fired for each transcription token */
|
|
596
|
-
caption: (data: CaptionData) => void;
|
|
597
|
-
/** Fired for each translated token */
|
|
598
|
-
translation: (data: TranslationData) => void;
|
|
599
|
-
/** Status changes (connecting, live, transcribing, etc.) */
|
|
600
|
-
status: (data: CaptionStatus) => void;
|
|
601
|
-
/** Credit balance updates */
|
|
602
|
-
credits: (data: CaptionCredits) => void;
|
|
603
|
-
/** Low credit warning */
|
|
604
|
-
credits_low: (data: {
|
|
605
|
-
remaining: number;
|
|
606
|
-
total: number;
|
|
607
|
-
percent: number;
|
|
608
|
-
}) => void;
|
|
609
|
-
/** Error events */
|
|
610
|
-
error: (data: CaptionError) => void;
|
|
611
|
-
/** WebSocket connected */
|
|
612
|
-
connected: () => void;
|
|
613
|
-
/** WebSocket disconnected */
|
|
614
|
-
disconnected: (code: number, reason: string) => void;
|
|
615
|
-
}
|
|
616
|
-
declare class TikTokCaptions extends EventEmitter {
|
|
617
|
-
private ws;
|
|
618
|
-
private _connected;
|
|
619
|
-
private intentionalClose;
|
|
620
|
-
private reconnectAttempts;
|
|
621
|
-
private readonly uniqueId;
|
|
622
|
-
private readonly apiKey;
|
|
623
|
-
private readonly serverUrl;
|
|
624
|
-
private readonly autoReconnect;
|
|
625
|
-
private readonly maxReconnectAttempts;
|
|
626
|
-
private readonly debug;
|
|
627
|
-
private readonly _translate;
|
|
628
|
-
private readonly _diarization;
|
|
629
|
-
private readonly _maxDurationMinutes;
|
|
630
|
-
private _language;
|
|
631
|
-
private streamAbortController;
|
|
632
|
-
private flvExtractor;
|
|
633
|
-
private streamUrl;
|
|
634
|
-
constructor(options: TikTokCaptionsOptions);
|
|
635
274
|
/**
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
* once the user goes live (or immediately if already live).
|
|
275
|
+
* Resolve unknown host user IDs via the sign server API.
|
|
276
|
+
* Caches results and re-emits the battleArmies event with enriched hostUser data.
|
|
639
277
|
*/
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
*/
|
|
644
|
-
stop(): void;
|
|
645
|
-
/**
|
|
646
|
-
* Switch the translation target language on-the-fly.
|
|
647
|
-
* Causes a brief interruption while the transcription engine reconfigures.
|
|
648
|
-
*/
|
|
649
|
-
setLanguage(language: string): void;
|
|
650
|
-
/**
|
|
651
|
-
* Request a credit balance update from the server.
|
|
652
|
-
*/
|
|
653
|
-
getCredits(): void;
|
|
654
|
-
/** Whether the WebSocket is currently connected */
|
|
655
|
-
get connected(): boolean;
|
|
656
|
-
/** The current target language */
|
|
657
|
-
get language(): string;
|
|
658
|
-
on<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this;
|
|
659
|
-
once<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this;
|
|
660
|
-
off<K extends keyof TikTokCaptionsEvents>(event: K, listener: TikTokCaptionsEvents[K]): this;
|
|
661
|
-
emit<K extends keyof TikTokCaptionsEvents>(event: K, ...args: Parameters<TikTokCaptionsEvents[K]>): boolean;
|
|
662
|
-
private buildWsUrl;
|
|
663
|
-
private send;
|
|
664
|
-
private handleMessage;
|
|
665
|
-
/**
|
|
666
|
-
* Connect to the TikTok FLV stream and extract audio.
|
|
667
|
-
* Sends binary audio buffers to the server via WebSocket.
|
|
668
|
-
*/
|
|
669
|
-
private connectToStream;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
interface GetRanklistOptions {
|
|
673
|
-
/** API server URL (default: https://api.tik.tools) */
|
|
674
|
-
serverUrl?: string;
|
|
675
|
-
/** API key for authentication */
|
|
676
|
-
apiKey: string;
|
|
677
|
-
/** TikTok username to look up (auto-resolves room_id and anchor_id) */
|
|
678
|
-
uniqueId?: string;
|
|
679
|
-
/** Direct room ID (skip resolution) */
|
|
680
|
-
roomId?: string;
|
|
681
|
-
/** Direct anchor/owner ID (skip resolution) */
|
|
682
|
-
anchorId?: string;
|
|
683
|
-
/**
|
|
684
|
-
* TikTok session cookie string for authentication.
|
|
685
|
-
* Required — ranklist endpoints return 20003 without login.
|
|
686
|
-
* Example: "sessionid=abc123; sid_guard=def456"
|
|
687
|
-
*/
|
|
688
|
-
sessionCookie?: string;
|
|
689
|
-
/**
|
|
690
|
-
* Which ranklist sub-endpoint to call:
|
|
691
|
-
* - "online_audience" (default) — top gifters with scores
|
|
692
|
-
* - "anchor_rank_list" — gifter ranking by rank_type
|
|
693
|
-
* - "entrance" — entrance UI metadata, tabs, gap-to-rank
|
|
694
|
-
*/
|
|
695
|
-
type?: 'online_audience' | 'anchor_rank_list' | 'entrance';
|
|
696
|
-
/**
|
|
697
|
-
* For "anchor_rank_list" type only:
|
|
698
|
-
* - "1" = hourly ranking (default)
|
|
699
|
-
* - "8" = daily ranking
|
|
700
|
-
*/
|
|
701
|
-
rankType?: string;
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* Fetch ranked user lists from TikTok via the sign server.
|
|
705
|
-
*
|
|
706
|
-
* When `sessionCookie` is provided, the server returns a **sign-and-return**
|
|
707
|
-
* response with a signed URL that you must fetch from your own IP
|
|
708
|
-
* (TikTok sessions are IP-bound). Check for `sign_and_return: true` in
|
|
709
|
-
* the response to detect this mode.
|
|
710
|
-
*
|
|
711
|
-
* @example
|
|
712
|
-
* ```ts
|
|
713
|
-
* const data = await getRanklist({
|
|
714
|
-
* apiKey: 'your-key',
|
|
715
|
-
* uniqueId: 'katarina.live',
|
|
716
|
-
* sessionCookie: 'sessionid=abc; sid_guard=def',
|
|
717
|
-
* type: 'online_audience',
|
|
718
|
-
* });
|
|
719
|
-
*
|
|
720
|
-
* if (data.sign_and_return) {
|
|
721
|
-
* // Fetch the signed URL from YOUR IP with your session cookie
|
|
722
|
-
* const resp = await fetch(data.signed_url, {
|
|
723
|
-
* method: data.method,
|
|
724
|
-
* headers: { ...data.headers, Cookie: sessionCookie },
|
|
725
|
-
* ...(data.body ? { body: data.body } : {}),
|
|
726
|
-
* });
|
|
727
|
-
* const tikData = await resp.json();
|
|
728
|
-
* console.log(tikData); // TikTok's raw response
|
|
729
|
-
* } else {
|
|
730
|
-
* console.log(data); // Direct TikTok response (no session)
|
|
731
|
-
* }
|
|
732
|
-
* ```
|
|
733
|
-
*/
|
|
734
|
-
declare function getRanklist(opts: GetRanklistOptions): Promise<RanklistResponse>;
|
|
735
|
-
|
|
736
|
-
interface GetRegionalRanklistOptions {
|
|
737
|
-
/** API server URL (default: https://api.tik.tools) */
|
|
738
|
-
serverUrl?: string;
|
|
739
|
-
/** API key for authentication (Pro or Ultra tier required) */
|
|
740
|
-
apiKey: string;
|
|
741
|
-
/** TikTok username to look up (auto-resolves room_id and anchor_id) */
|
|
742
|
-
uniqueId?: string;
|
|
743
|
-
/** Direct room ID (skip resolution) */
|
|
744
|
-
roomId?: string;
|
|
745
|
-
/** Direct anchor/owner ID (skip resolution) */
|
|
746
|
-
anchorId?: string;
|
|
747
|
-
/**
|
|
748
|
-
* Ranking period:
|
|
749
|
-
* - "1" = Hourly
|
|
750
|
-
* - "8" = Daily (default)
|
|
751
|
-
* - "15" = Popular LIVE
|
|
752
|
-
* - "16" = League
|
|
753
|
-
*/
|
|
754
|
-
rankType?: '1' | '8' | '15' | '16';
|
|
755
|
-
/**
|
|
756
|
-
* Sub-endpoint type:
|
|
757
|
-
* - "list" (default) — ranked users with scores
|
|
758
|
-
* - "entrance" — available ranking tabs/metadata
|
|
759
|
-
*/
|
|
760
|
-
type?: 'list' | 'entrance';
|
|
761
|
-
/** Gap interval filter (default: "0") */
|
|
762
|
-
gapInterval?: string;
|
|
763
|
-
/**
|
|
764
|
-
* TikTok session cookie string for authentication.
|
|
765
|
-
* Passed to the API server for proxied requests.
|
|
766
|
-
* Example: "sessionid=abc123; sessionid_ss=abc123"
|
|
767
|
-
*/
|
|
768
|
-
sessionCookie?: string;
|
|
769
|
-
/**
|
|
770
|
-
* Client's real IP address (for deployed server scenarios).
|
|
771
|
-
* When running on a remote server, pass the end-user's IP so the
|
|
772
|
-
* API server can proxy the TikTok request from the correct IP.
|
|
773
|
-
*/
|
|
774
|
-
clientIp?: string;
|
|
775
|
-
}
|
|
776
|
-
interface RegionalRanklistSignedResponse {
|
|
777
|
-
/** Always 0 on success */
|
|
778
|
-
status_code: number;
|
|
779
|
-
/** Always "fetch_signed_url" */
|
|
780
|
-
action: string;
|
|
781
|
-
/** The signed TikTok URL to POST */
|
|
782
|
-
signed_url: string;
|
|
783
|
-
/** HTTP method (always POST) */
|
|
784
|
-
method: string;
|
|
785
|
-
/** Required headers for the fetch */
|
|
786
|
-
headers: Record<string, string>;
|
|
787
|
-
/** URL-encoded POST body */
|
|
788
|
-
body: string;
|
|
789
|
-
/** Cookies to include (ttwid etc.) — append your sessionid */
|
|
790
|
-
cookies: string;
|
|
791
|
-
/** Human-readable note */
|
|
792
|
-
note: string;
|
|
793
|
-
}
|
|
794
|
-
/**
|
|
795
|
-
* Get a signed URL for fetching regional LIVE leaderboard data.
|
|
796
|
-
*
|
|
797
|
-
* **Two-step pattern**: TikTok sessions are IP-bound, so instead of
|
|
798
|
-
* server-side fetching, this returns a signed URL with headers/body
|
|
799
|
-
* that you POST from your own IP with your session cookie.
|
|
800
|
-
*
|
|
801
|
-
* Requires **Pro** or **Ultra** API key tier.
|
|
802
|
-
*
|
|
803
|
-
* @example
|
|
804
|
-
* ```ts
|
|
805
|
-
* // Step 1: Get signed URL
|
|
806
|
-
* const signed = await getRegionalRanklist({
|
|
807
|
-
* apiKey: 'your-pro-key',
|
|
808
|
-
* roomId: '7607695933891218198',
|
|
809
|
-
* anchorId: '7444599004337652758',
|
|
810
|
-
* rankType: '8', // Daily
|
|
811
|
-
* });
|
|
812
|
-
*
|
|
813
|
-
* // Step 2: Fetch from YOUR IP with YOUR session
|
|
814
|
-
* const resp = await fetch(signed.signed_url, {
|
|
815
|
-
* method: signed.method,
|
|
816
|
-
* headers: { ...signed.headers, Cookie: `sessionid=YOUR_SID; ${signed.cookies}` },
|
|
817
|
-
* body: signed.body,
|
|
818
|
-
* });
|
|
819
|
-
* const { data } = await resp.json();
|
|
820
|
-
* data.rank_view.ranks.forEach((r, i) =>
|
|
821
|
-
* console.log(`${i+1}. ${r.user.nickname} — ${r.score} pts`)
|
|
822
|
-
* );
|
|
823
|
-
* ```
|
|
824
|
-
*/
|
|
825
|
-
declare function getRegionalRanklist(opts: GetRegionalRanklistOptions): Promise<RegionalRanklistSignedResponse>;
|
|
826
|
-
|
|
827
|
-
interface GetLiveFeedOptions {
|
|
828
|
-
/** API server URL (default: https://api.tik.tools) */
|
|
829
|
-
serverUrl?: string;
|
|
830
|
-
/** API key for authentication (Pro or Ultra tier required) */
|
|
831
|
-
apiKey: string;
|
|
832
|
-
/** Region code (default: 'US') */
|
|
833
|
-
region?: string;
|
|
834
|
-
/**
|
|
835
|
-
* Feed channel:
|
|
836
|
-
* - '87' = Recommended (default)
|
|
837
|
-
* - '86' = Suggested
|
|
838
|
-
* - '42' = Following
|
|
839
|
-
* - '1111006' = Gaming
|
|
840
|
-
*/
|
|
841
|
-
channelId?: string;
|
|
842
|
-
/** Number of rooms to return (max 50, default 20) */
|
|
843
|
-
count?: number;
|
|
844
|
-
/** Pagination cursor from previous response (default: '0') */
|
|
845
|
-
maxTime?: string;
|
|
846
|
-
/** TikTok sessionid cookie — required for populated results */
|
|
847
|
-
sessionId?: string;
|
|
848
|
-
/** TikTok ttwid cookie */
|
|
849
|
-
ttwid?: string;
|
|
850
|
-
/** TikTok msToken cookie */
|
|
851
|
-
msToken?: string;
|
|
278
|
+
private resolveHostUsers;
|
|
279
|
+
private startHeartbeat;
|
|
280
|
+
private stopHeartbeat;
|
|
852
281
|
}
|
|
853
|
-
/**
|
|
854
|
-
* Get a signed URL for fetching the TikTok LIVE feed.
|
|
855
|
-
*
|
|
856
|
-
* **Two-step pattern**: Returns a signed URL with headers and cookies.
|
|
857
|
-
* Fetch the signed URL from your own IP to get the feed data.
|
|
858
|
-
*
|
|
859
|
-
* Requires **Pro** or **Ultra** API key tier.
|
|
860
|
-
*
|
|
861
|
-
* @example
|
|
862
|
-
* ```ts
|
|
863
|
-
* // Step 1: Get signed URL
|
|
864
|
-
* const signed = await getLiveFeed({
|
|
865
|
-
* apiKey: 'your-pro-key',
|
|
866
|
-
* sessionId: 'your-tiktok-sessionid',
|
|
867
|
-
* region: 'US',
|
|
868
|
-
* count: 10,
|
|
869
|
-
* });
|
|
870
|
-
*
|
|
871
|
-
* // Step 2: Fetch from YOUR IP
|
|
872
|
-
* const resp = await fetch(signed.signed_url, {
|
|
873
|
-
* headers: { ...signed.headers, Cookie: signed.cookies || '' },
|
|
874
|
-
* });
|
|
875
|
-
* const data = await resp.json();
|
|
876
|
-
* console.log(`Found ${data.data?.length || 0} live rooms`);
|
|
877
|
-
*
|
|
878
|
-
* // Step 3: Load more (pagination)
|
|
879
|
-
* const nextSigned = await getLiveFeed({
|
|
880
|
-
* apiKey: 'your-pro-key',
|
|
881
|
-
* sessionId: 'your-tiktok-sessionid',
|
|
882
|
-
* maxTime: data.extra?.max_time || '0',
|
|
883
|
-
* });
|
|
884
|
-
* ```
|
|
885
|
-
*/
|
|
886
|
-
declare function getLiveFeed(opts: GetLiveFeedOptions): Promise<FeedSignedResponse>;
|
|
887
|
-
/**
|
|
888
|
-
* Convenience: Get the feed AND fetch the signed URL in one step.
|
|
889
|
-
* Returns the parsed JSON feed data from TikTok.
|
|
890
|
-
*
|
|
891
|
-
* @example
|
|
892
|
-
* ```ts
|
|
893
|
-
* const feed = await fetchFeed({
|
|
894
|
-
* apiKey: 'your-pro-key',
|
|
895
|
-
* sessionId: 'your-tiktok-sessionid',
|
|
896
|
-
* region: 'GR',
|
|
897
|
-
* count: 10,
|
|
898
|
-
* });
|
|
899
|
-
* for (const entry of feed.data || []) {
|
|
900
|
-
* const room = entry.data;
|
|
901
|
-
* console.log(`🔴 @${room.owner.display_id}: "${room.title}" — ${room.user_count} viewers`);
|
|
902
|
-
* }
|
|
903
|
-
* ```
|
|
904
|
-
*/
|
|
905
|
-
declare function fetchFeed(opts: GetLiveFeedOptions): Promise<any>;
|
|
906
282
|
|
|
907
|
-
export { type
|
|
283
|
+
export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTeam, type BattleTeamUser, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type StreamInfo, type StreamQuality, type StreamUrls, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
|