@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/dist/index.d.ts 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,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
- 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). 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 | BattleTaskEvent | BarrageEvent | SubscribeEvent | EmoteChatEvent | EnvelopeEvent | QuestionEvent | ControlEvent | RoomEvent | LiveIntroEvent | RankUpdateEvent | LinkMicEvent | UnknownEvent;
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
- ownerUserId?: string;
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
- 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
- */
207
+ /** Pre-resolved room ID — skips direct TikTok page fetch when provided with sessionId */
237
208
  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;
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 _ownerUserId;
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 _sessionId?;
459
- private _ttTargetIdc?;
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
- * Build a cookie header string for authenticated API requests (e.g. ranklist).
475
- * Returns undefined if no session is set.
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
- buildSessionCookieHeader(): string | undefined;
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
- * 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).
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
- 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;
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 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 };
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 };