@tiktool/live 2.6.7 → 2.7.0

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/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
- profilePictureUrl?: string;
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,18 @@ 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
- battleSettings?: {
86
- startTimeMs?: number;
87
- duration?: number;
88
- endTimeMs?: number;
89
- };
90
- scoreUpdateTime?: number;
91
- giftSentTime?: number;
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) */
87
+ timeLeftSeconds: number;
113
88
  }
114
89
  interface SubscribeEvent extends BaseEvent {
115
90
  type: 'subscribe';
@@ -121,7 +96,6 @@ interface EmoteChatEvent extends BaseEvent {
121
96
  user: TikTokUser;
122
97
  emoteId: string;
123
98
  emoteUrl: string;
124
- emoteName?: string;
125
99
  }
126
100
  interface EnvelopeEvent extends BaseEvent {
127
101
  type: 'envelope';
@@ -164,7 +138,7 @@ interface UnknownEvent extends BaseEvent {
164
138
  type: 'unknown';
165
139
  method: string;
166
140
  }
167
- type LiveEvent = ChatEvent | MemberEvent | LikeEvent | GiftEvent | SocialEvent | RoomUserSeqEvent | BattleEvent | BattleArmiesEvent | BattleTaskEvent | BarrageEvent | SubscribeEvent | EmoteChatEvent | EnvelopeEvent | QuestionEvent | ControlEvent | RoomEvent | LiveIntroEvent | RankUpdateEvent | LinkMicEvent | UnknownEvent;
141
+ type LiveEvent = ChatEvent | MemberEvent | LikeEvent | GiftEvent | SocialEvent | RoomUserSeqEvent | BattleEvent | BattleArmiesEvent | SubscribeEvent | EmoteChatEvent | EnvelopeEvent | QuestionEvent | ControlEvent | RoomEvent | LiveIntroEvent | RankUpdateEvent | LinkMicEvent | UnknownEvent;
168
142
  interface TikTokLiveEvents {
169
143
  connected: () => void;
170
144
  disconnected: (code: number, reason: string) => void;
@@ -178,8 +152,6 @@ interface TikTokLiveEvents {
178
152
  roomUserSeq: (event: RoomUserSeqEvent) => void;
179
153
  battle: (event: BattleEvent) => void;
180
154
  battleArmies: (event: BattleArmiesEvent) => void;
181
- battleTask: (event: BattleTaskEvent) => void;
182
- barrage: (event: BarrageEvent) => void;
183
155
  subscribe: (event: SubscribeEvent) => void;
184
156
  emoteChat: (event: EmoteChatEvent) => void;
185
157
  envelope: (event: EnvelopeEvent) => void;
@@ -197,7 +169,30 @@ interface RoomInfo {
197
169
  wsHost: string;
198
170
  clusterRegion: string;
199
171
  connectedAt: string;
200
- ownerUserId?: string;
172
+ }
173
+ /** Quality tiers available for live stream video */
174
+ type StreamQuality = 'FULL_HD1' | 'HD1' | 'SD1' | 'SD2' | 'origin' | string;
175
+ /** Stream URLs for a specific quality level */
176
+ interface StreamUrls {
177
+ /** FLV pull URL for this quality */
178
+ flv?: string;
179
+ /** HLS (m3u8) pull URL for this quality */
180
+ hls?: string;
181
+ }
182
+ /** Full stream info returned by getStreamUrl() */
183
+ interface StreamInfo {
184
+ /** Room ID of the live stream */
185
+ roomId: string;
186
+ /** Whether the user is currently live */
187
+ alive: boolean;
188
+ /** Stream URLs keyed by quality (FULL_HD1, HD1, SD1, SD2) */
189
+ streamUrls: Record<StreamQuality, StreamUrls>;
190
+ /** Recommended default quality */
191
+ defaultQuality: StreamQuality;
192
+ /** Best available FLV URL (convenience shortcut) */
193
+ flvPullUrl?: string;
194
+ /** Best available HLS URL (convenience shortcut) */
195
+ hlsPullUrl?: string;
201
196
  }
202
197
  interface TikTokLiveOptions {
203
198
  uniqueId: string;
@@ -207,236 +202,10 @@ interface TikTokLiveOptions {
207
202
  maxReconnectAttempts?: number;
208
203
  heartbeatInterval?: number;
209
204
  debug?: boolean;
210
- webSocketImpl?: any;
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
- */
205
+ /** Pre-resolved room ID — skips direct TikTok page fetch when provided with sessionId */
237
206
  roomId?: string;
238
- /**
239
- * Pre-fetched ttwid cookie. When provided along with roomId,
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;
207
+ /** Pre-resolved ttwid session cookie — skips direct TikTok page fetch when provided with roomId */
208
+ sessionId?: string;
440
209
  }
441
210
 
442
211
  declare class TikTokLive extends EventEmitter {
@@ -445,9 +214,12 @@ declare class TikTokLive extends EventEmitter {
445
214
  private reconnectAttempts;
446
215
  private intentionalClose;
447
216
  private _connected;
217
+ private _destroyed;
448
218
  private _eventCount;
449
219
  private _roomId;
450
- private _ownerUserId;
220
+ private static readonly MAX_BATTLE_HOSTS;
221
+ private _battleHosts;
222
+ private _pendingHostResolves;
451
223
  private readonly uniqueId;
452
224
  private readonly signServerUrl;
453
225
  private readonly apiKey;
@@ -455,453 +227,55 @@ declare class TikTokLive extends EventEmitter {
455
227
  private readonly maxReconnectAttempts;
456
228
  private readonly heartbeatInterval;
457
229
  private readonly debug;
458
- private _sessionId?;
459
- private _ttTargetIdc?;
460
- private readonly proxyAgent?;
461
- private readonly _presetRoomId?;
462
- private readonly _presetTtwid?;
230
+ private readonly _presetRoomId;
231
+ private readonly _presetSessionId;
463
232
  constructor(options: TikTokLiveOptions);
464
233
  connect(): Promise<void>;
465
234
  disconnect(): void;
235
+ /**
236
+ * Fully destroy the client, releasing all resources and listeners.
237
+ * After calling destroy(), the instance cannot be reused — create a new one.
238
+ */
239
+ destroy(): void;
466
240
  get connected(): boolean;
241
+ get destroyed(): boolean;
467
242
  get eventCount(): number;
468
243
  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
244
  /**
474
- * Build a cookie header string for authenticated API requests (e.g. ranklist).
475
- * Returns undefined if no session is set.
245
+ * Get live stream video URLs for a TikTok user.
246
+ * Returns FLV & HLS URLs segmented by quality (FULL_HD1, HD1, SD1, SD2).
247
+ * This is a standalone method — no WebSocket connection required.
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * const stream = await TikTokLive.getStreamUrl({
252
+ * uniqueId: 'username',
253
+ * apiKey: 'your-api-key',
254
+ * });
255
+ * if (stream.alive) {
256
+ * console.log(stream.streamUrls.HD1?.hls); // HLS URL for HD quality
257
+ * console.log(stream.flvPullUrl); // Best FLV URL
258
+ * }
259
+ * ```
476
260
  */
477
- buildSessionCookieHeader(): string | undefined;
261
+ static getStreamUrl(options: {
262
+ uniqueId: string;
263
+ apiKey: string;
264
+ signServerUrl?: string;
265
+ quality?: string;
266
+ }): Promise<StreamInfo>;
478
267
  on<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
479
268
  once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
480
269
  off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
481
270
  emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean;
482
271
  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
272
  /**
636
- * Start real-time captions for the configured TikTok user.
637
- * Connects to the captions WebSocket relay and begins transcription
638
- * once the user goes live (or immediately if already live).
273
+ * Resolve unknown host user IDs via the sign server API.
274
+ * Caches results and re-emits the battleArmies event with enriched hostUser data.
639
275
  */
640
- start(): Promise<void>;
641
- /**
642
- * Stop captions and disconnect.
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;
276
+ private resolveHostUsers;
277
+ private startHeartbeat;
278
+ private stopHeartbeat;
852
279
  }
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
280
 
907
- export { type AnchorRankListEntry, type AnchorRankListResponse, type BarrageEvent, type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTaskEvent, type BattleTeam, type BattleTeamUser, type CaptionCredits, type CaptionData, type CaptionError, type CaptionStatus, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EntranceInfo, type EntranceResponse, type EntranceTab, type EnvelopeEvent, type FeedRoom, type FeedRoomOwner, type FeedSignedResponse, type GetLiveFeedOptions, type GetRanklistOptions, type GetRegionalRanklistOptions, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type OnlineAudienceEntry, type OnlineAudienceResponse, type QuestionEvent, type RankUpdateEvent, type RanklistResponse, type RanklistSelfInfo, type RanklistUser, type RegionalRanklistSignedResponse, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type SubscribeEvent, TikTokCaptions, type TikTokCaptionsEvents, type TikTokCaptionsOptions, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type TranslationData, type UnknownEvent, fetchFeed, getLiveFeed, getRanklist, getRegionalRanklist };
281
+ 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 };