@tiktool/live 2.2.1 → 2.3.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 CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  **Real-time TikTok LIVE events — chat messages, gifts, follows, likes, battles, viewer counts, and 18+ event types — streamed directly to your application via WebSocket.**
14
14
 
15
- [Quick Start](#-quick-start) · [Events](#-events) · [REST API](#-rest-api-endpoints) · [Pricing](#-pricing) · [Get API Key](https://tik.tools)
15
+ [Quick Start](#-quick-start) · [Events](#-events) · [API Utilities](#-api-utilities) · [REST API](#-rest-api-endpoints) · [Pricing](#-pricing) · [Get API Key](https://tik.tools)
16
16
 
17
17
  </div>
18
18
 
@@ -122,9 +122,102 @@ live.on('event', (event) => {
122
122
 
123
123
  ---
124
124
 
125
+ ## 🛠️ API Utilities
126
+
127
+ The SDK includes server-side utilities for calling TikTool REST endpoints. These handle the **sign-and-return** flow automatically — resolving usernames to room IDs, fetching signed URLs, and returning actual TikTok data.
128
+
129
+ ### `callApi(options)` — High-Level Helper
130
+
131
+ The easiest way to call any TikTool endpoint. Handles the full flow automatically:
132
+
133
+ ```typescript
134
+ import { callApi } from '@tiktool/live';
135
+
136
+ // Get stream video URLs for a user
137
+ const streamData = await callApi({
138
+ apiKey: 'YOUR_API_KEY',
139
+ endpoint: '/webcast/room_video',
140
+ uniqueId: 'streamer_name',
141
+ });
142
+
143
+ // Get room info
144
+ const roomInfo = await callApi({
145
+ apiKey: 'YOUR_API_KEY',
146
+ endpoint: '/webcast/room_info',
147
+ uniqueId: 'streamer_name',
148
+ });
149
+
150
+ // Use a custom server URL
151
+ const data = await callApi({
152
+ serverUrl: 'https://your-server.com',
153
+ apiKey: 'YOUR_API_KEY',
154
+ endpoint: '/webcast/rankings',
155
+ uniqueId: 'streamer_name',
156
+ method: 'GET',
157
+ });
158
+ ```
159
+
160
+ #### `CallApiOptions`
161
+
162
+ | Option | Type | Default | Description |
163
+ |--------|------|---------|-------------|
164
+ | `apiKey` | `string` | — | API key from [tik.tools](https://tik.tools) |
165
+ | `endpoint` | `string` | — | API path (e.g. `'/webcast/room_video'`) |
166
+ | `uniqueId` | `string` | — | TikTok username to resolve |
167
+ | `serverUrl` | `string` | `https://api.tik.tools` | Custom API server URL |
168
+ | `method` | `'GET' \| 'POST'` | `'POST'` | HTTP method |
169
+ | `extraBody` | `object` | — | Additional POST body fields |
170
+
171
+ ### `resolveRoomId(uniqueId)` — Username to Room ID
172
+
173
+ Scrapes a TikTok live page to extract the `room_id`. Results are cached for 5 minutes. Returns `null` if the user is not live.
174
+
175
+ ```typescript
176
+ import { resolveRoomId } from '@tiktool/live';
177
+
178
+ const roomId = await resolveRoomId('streamer_name');
179
+ if (roomId) {
180
+ console.log(`Room ID: ${roomId}`);
181
+ }
182
+ ```
183
+
184
+ ### `resolveLivePage(uniqueId)` — Full Live Page Resolution
185
+
186
+ Returns all live page metadata including the room ID, session cookie (`ttwid`), and cluster region. Used internally by `TikTokLive.connect()`. Returns `null` if the user is not live.
187
+
188
+ ```typescript
189
+ import { resolveLivePage } from '@tiktool/live';
190
+
191
+ const info = await resolveLivePage('streamer_name');
192
+ if (info) {
193
+ console.log(`Room: ${info.roomId}, Region: ${info.clusterRegion}`);
194
+ }
195
+ ```
196
+
197
+ #### `LivePageInfo`
198
+
199
+ | Field | Type | Description |
200
+ |-------|------|-------------|
201
+ | `roomId` | `string` | Active room ID for the livestream |
202
+ | `ttwid` | `string` | Session cookie for WebSocket auth |
203
+ | `clusterRegion` | `string` | Server cluster region (e.g. `'us'`, `'eu'`) |
204
+
205
+ ### `fetchSignedUrl(response)` — Execute Signed URL Response
206
+
207
+ Takes a sign-and-return API response and fetches the actual TikTok data from the signed URL.
208
+
209
+ ```typescript
210
+ import { fetchSignedUrl } from '@tiktool/live';
211
+
212
+ // After calling an API endpoint that returns action: 'fetch_signed_url'
213
+ const tikTokData = await fetchSignedUrl(apiResponse);
214
+ ```
215
+
216
+ ---
217
+
125
218
  ## 🌐 REST API Endpoints
126
219
 
127
- The sign server at `api.tik.tools` exposes a full REST API alongside WebSocket signing. Basic endpoints (signing, live checks) are available on the free tier. Pro and Ultra plans unlock all endpoints with higher rate limits.
220
+ The sign server at `api.tik.tools` exposes a full REST API alongside WebSocket signing. Endpoints use a **sign-and-return** pattern when called with a `unique_id`, they return instructions for resolving the room ID; when called with a `room_id`, they return a signed URL to fetch data directly from TikTok. Use `callApi()` to handle this automatically.
128
221
 
129
222
  | Endpoint | Description |
130
223
  |----------|-------------|
package/dist/index.d.mts CHANGED
@@ -219,4 +219,71 @@ declare class TikTokLive extends TypedEmitter {
219
219
  private stopHeartbeat;
220
220
  }
221
221
 
222
- 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 SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
222
+ /**
223
+ * TikTool API utilities for the sign-and-return API flow.
224
+ *
225
+ * The API server returns signed URLs instead of fetching TikTok data directly.
226
+ * These utilities handle the two-step flow:
227
+ * 1. resolve_required — scrape TikTok HTML to get room_id
228
+ * 2. fetch_signed_url — use the signed URL to get actual TikTok data
229
+ *
230
+ * @module
231
+ */
232
+ /**
233
+ * Resolved live page metadata from a TikTok live page.
234
+ */
235
+ interface LivePageInfo {
236
+ /** Active room ID for the livestream */
237
+ roomId: string;
238
+ /** Session cookie required for WebSocket authentication */
239
+ ttwid: string;
240
+ /** Server cluster region (e.g. 'us', 'eu') */
241
+ clusterRegion: string;
242
+ }
243
+ /**
244
+ * Scrape a TikTok live page to extract room metadata.
245
+ * Returns the room ID, session cookie, and cluster region.
246
+ * Results are cached for 5 minutes. Returns `null` if the user is not live.
247
+ */
248
+ declare function resolveLivePage(uniqueId: string): Promise<LivePageInfo | null>;
249
+ /**
250
+ * Resolve a TikTok username to a room ID.
251
+ * Returns `null` if the user is not currently live.
252
+ * Results are cached for 5 minutes.
253
+ */
254
+ declare function resolveRoomId(uniqueId: string): Promise<string | null>;
255
+ interface SignedUrlResponse {
256
+ status_code: number;
257
+ action: string;
258
+ signed_url: string;
259
+ headers: Record<string, string>;
260
+ cookies: string;
261
+ }
262
+ /**
263
+ * Execute a signed-URL API response: fetch the signed URL and return the
264
+ * parsed JSON data from TikTok.
265
+ */
266
+ declare function fetchSignedUrl(response: SignedUrlResponse): Promise<any>;
267
+ interface CallApiOptions {
268
+ /** API server URL (default: https://api.tik.tools) */
269
+ serverUrl?: string;
270
+ /** API key for authentication */
271
+ apiKey: string;
272
+ /** API endpoint path (e.g. '/webcast/room_video') */
273
+ endpoint: string;
274
+ /** TikTok unique_id to resolve */
275
+ uniqueId: string;
276
+ /** HTTP method (default: POST) */
277
+ method?: 'GET' | 'POST';
278
+ /** Additional body fields for POST requests */
279
+ extraBody?: Record<string, any>;
280
+ }
281
+ /**
282
+ * Call a TikTool API endpoint, handling the full
283
+ * resolve_required → room_id → signed_url → fetch flow automatically.
284
+ *
285
+ * Returns the actual TikTok data, or `null` if the user is not live.
286
+ */
287
+ declare function callApi(opts: CallApiOptions): Promise<any>;
288
+
289
+ export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTeam, type BattleTeamUser, type CallApiOptions, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type LivePageInfo, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SignedUrlResponse, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent, callApi, fetchSignedUrl, resolveLivePage, resolveRoomId };
package/dist/index.d.ts CHANGED
@@ -219,4 +219,71 @@ declare class TikTokLive extends TypedEmitter {
219
219
  private stopHeartbeat;
220
220
  }
221
221
 
222
- 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 SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
222
+ /**
223
+ * TikTool API utilities for the sign-and-return API flow.
224
+ *
225
+ * The API server returns signed URLs instead of fetching TikTok data directly.
226
+ * These utilities handle the two-step flow:
227
+ * 1. resolve_required — scrape TikTok HTML to get room_id
228
+ * 2. fetch_signed_url — use the signed URL to get actual TikTok data
229
+ *
230
+ * @module
231
+ */
232
+ /**
233
+ * Resolved live page metadata from a TikTok live page.
234
+ */
235
+ interface LivePageInfo {
236
+ /** Active room ID for the livestream */
237
+ roomId: string;
238
+ /** Session cookie required for WebSocket authentication */
239
+ ttwid: string;
240
+ /** Server cluster region (e.g. 'us', 'eu') */
241
+ clusterRegion: string;
242
+ }
243
+ /**
244
+ * Scrape a TikTok live page to extract room metadata.
245
+ * Returns the room ID, session cookie, and cluster region.
246
+ * Results are cached for 5 minutes. Returns `null` if the user is not live.
247
+ */
248
+ declare function resolveLivePage(uniqueId: string): Promise<LivePageInfo | null>;
249
+ /**
250
+ * Resolve a TikTok username to a room ID.
251
+ * Returns `null` if the user is not currently live.
252
+ * Results are cached for 5 minutes.
253
+ */
254
+ declare function resolveRoomId(uniqueId: string): Promise<string | null>;
255
+ interface SignedUrlResponse {
256
+ status_code: number;
257
+ action: string;
258
+ signed_url: string;
259
+ headers: Record<string, string>;
260
+ cookies: string;
261
+ }
262
+ /**
263
+ * Execute a signed-URL API response: fetch the signed URL and return the
264
+ * parsed JSON data from TikTok.
265
+ */
266
+ declare function fetchSignedUrl(response: SignedUrlResponse): Promise<any>;
267
+ interface CallApiOptions {
268
+ /** API server URL (default: https://api.tik.tools) */
269
+ serverUrl?: string;
270
+ /** API key for authentication */
271
+ apiKey: string;
272
+ /** API endpoint path (e.g. '/webcast/room_video') */
273
+ endpoint: string;
274
+ /** TikTok unique_id to resolve */
275
+ uniqueId: string;
276
+ /** HTTP method (default: POST) */
277
+ method?: 'GET' | 'POST';
278
+ /** Additional body fields for POST requests */
279
+ extraBody?: Record<string, any>;
280
+ }
281
+ /**
282
+ * Call a TikTool API endpoint, handling the full
283
+ * resolve_required → room_id → signed_url → fetch flow automatically.
284
+ *
285
+ * Returns the actual TikTok data, or `null` if the user is not live.
286
+ */
287
+ declare function callApi(opts: CallApiOptions): Promise<any>;
288
+
289
+ export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTeam, type BattleTeamUser, type CallApiOptions, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type LivePageInfo, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SignedUrlResponse, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent, callApi, fetchSignedUrl, resolveLivePage, resolveRoomId };
package/dist/index.js CHANGED
@@ -30,7 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- TikTokLive: () => TikTokLive
33
+ TikTokLive: () => TikTokLive,
34
+ callApi: () => callApi,
35
+ fetchSignedUrl: () => fetchSignedUrl,
36
+ resolveLivePage: () => resolveLivePage,
37
+ resolveRoomId: () => resolveRoomId
34
38
  });
35
39
  module.exports = __toCommonJS(index_exports);
36
40
 
@@ -315,7 +319,8 @@ function parseWebcastMessage(method, payload) {
315
319
  repeatEnd,
316
320
  combo: repeatCount > 1 && !repeatEnd,
317
321
  giftType,
318
- groupId
322
+ groupId,
323
+ giftPictureUrl: giftImageUrl
319
324
  };
320
325
  }
321
326
  case "WebcastSocialMessage": {
@@ -495,9 +500,140 @@ function parseWebcastResponse(payload) {
495
500
  return events;
496
501
  }
497
502
 
498
- // src/client.ts
503
+ // src/api.ts
499
504
  var DEFAULT_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
500
505
  var DEFAULT_SIGN_SERVER = "https://api.tik.tools";
506
+ var pageCache = /* @__PURE__ */ new Map();
507
+ var PAGE_CACHE_TTL = 5 * 60 * 1e3;
508
+ async function resolveLivePage(uniqueId) {
509
+ const clean = uniqueId.replace(/^@/, "");
510
+ const cached = pageCache.get(clean);
511
+ if (cached && Date.now() - cached.ts < PAGE_CACHE_TTL) {
512
+ return cached.info;
513
+ }
514
+ try {
515
+ const resp = await fetch(`https://www.tiktok.com/@${clean}/live`, {
516
+ headers: {
517
+ "User-Agent": DEFAULT_UA,
518
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
519
+ "Accept-Language": "en-US,en;q=0.9"
520
+ },
521
+ redirect: "follow"
522
+ });
523
+ if (!resp.ok) return null;
524
+ let ttwid = "";
525
+ const setCookies = resp.headers.get("set-cookie") || "";
526
+ for (const part of setCookies.split(",")) {
527
+ const trimmed = part.trim();
528
+ if (trimmed.startsWith("ttwid=")) {
529
+ ttwid = trimmed.split(";")[0].split("=").slice(1).join("=");
530
+ break;
531
+ }
532
+ }
533
+ if (!ttwid && typeof resp.headers.getSetCookie === "function") {
534
+ for (const sc of resp.headers.getSetCookie()) {
535
+ if (typeof sc === "string" && sc.startsWith("ttwid=")) {
536
+ ttwid = sc.split(";")[0].split("=").slice(1).join("=");
537
+ break;
538
+ }
539
+ }
540
+ }
541
+ const html = await resp.text();
542
+ let roomId = "";
543
+ const sigiMatch = html.match(/id="SIGI_STATE"[^>]*>([^<]+)/);
544
+ if (sigiMatch) {
545
+ try {
546
+ const json = JSON.parse(sigiMatch[1]);
547
+ const jsonStr = JSON.stringify(json);
548
+ const m = jsonStr.match(/"roomId"\s*:\s*"(\d+)"/);
549
+ if (m) roomId = m[1];
550
+ } catch {
551
+ }
552
+ }
553
+ if (!roomId) {
554
+ const patterns = [
555
+ /"roomId"\s*:\s*"(\d+)"/,
556
+ /room_id[=/](\d{10,})/,
557
+ /"idStr"\s*:\s*"(\d{10,})"/
558
+ ];
559
+ for (const p of patterns) {
560
+ const m = html.match(p);
561
+ if (m) {
562
+ roomId = m[1];
563
+ break;
564
+ }
565
+ }
566
+ }
567
+ if (!roomId) return null;
568
+ const crMatch = html.match(/"clusterRegion"\s*:\s*"([^"]+)"/);
569
+ const clusterRegion = crMatch ? crMatch[1] : "";
570
+ const info = { roomId, ttwid, clusterRegion };
571
+ pageCache.set(clean, { info, ts: Date.now() });
572
+ return info;
573
+ } catch {
574
+ }
575
+ return null;
576
+ }
577
+ async function resolveRoomId(uniqueId) {
578
+ const info = await resolveLivePage(uniqueId);
579
+ return info?.roomId ?? null;
580
+ }
581
+ async function fetchSignedUrl(response) {
582
+ if (response.action !== "fetch_signed_url" || !response.signed_url) {
583
+ return null;
584
+ }
585
+ const headers = { ...response.headers || {} };
586
+ if (response.cookies) {
587
+ headers["Cookie"] = response.cookies;
588
+ }
589
+ const resp = await fetch(response.signed_url, { headers, redirect: "follow" });
590
+ const text = await resp.text();
591
+ try {
592
+ return JSON.parse(text);
593
+ } catch {
594
+ return null;
595
+ }
596
+ }
597
+ async function callApi(opts) {
598
+ const serverUrl = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\/$/, "");
599
+ const isGet = opts.method === "GET";
600
+ const ak = encodeURIComponent(opts.apiKey);
601
+ const url1 = isGet ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&unique_id=${encodeURIComponent(opts.uniqueId)}` : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;
602
+ const fetchOpts1 = isGet ? {} : {
603
+ method: "POST",
604
+ headers: { "Content-Type": "application/json" },
605
+ body: JSON.stringify({ unique_id: opts.uniqueId, ...opts.extraBody })
606
+ };
607
+ const resp1 = await fetch(url1, fetchOpts1);
608
+ const data1 = await resp1.json();
609
+ if (data1.status_code === 0 && !["resolve_required", "fetch_signed_url"].includes(data1.action)) {
610
+ return data1;
611
+ }
612
+ if (data1.action === "fetch_signed_url") {
613
+ return fetchSignedUrl(data1);
614
+ }
615
+ if (data1.action === "resolve_required") {
616
+ const roomId = await resolveRoomId(opts.uniqueId);
617
+ if (!roomId) return null;
618
+ const url2 = isGet ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&room_id=${encodeURIComponent(roomId)}` : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;
619
+ const fetchOpts2 = isGet ? {} : {
620
+ method: "POST",
621
+ headers: { "Content-Type": "application/json" },
622
+ body: JSON.stringify({ room_id: roomId, ...opts.extraBody })
623
+ };
624
+ const resp2 = await fetch(url2, fetchOpts2);
625
+ const data2 = await resp2.json();
626
+ if (data2.action === "fetch_signed_url") {
627
+ return fetchSignedUrl(data2);
628
+ }
629
+ return data2;
630
+ }
631
+ return data1;
632
+ }
633
+
634
+ // src/client.ts
635
+ var DEFAULT_UA2 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
636
+ var DEFAULT_SIGN_SERVER2 = "https://api.tik.tools";
501
637
  var TypedEmitter = class {
502
638
  _listeners = /* @__PURE__ */ new Map();
503
639
  on(event, fn) {
@@ -582,7 +718,7 @@ var TikTokLive = class extends TypedEmitter {
582
718
  constructor(options) {
583
719
  super();
584
720
  this.uniqueId = options.uniqueId.replace(/^@/, "");
585
- this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\/$/, "");
721
+ this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER2).replace(/\/$/, "");
586
722
  if (!options.apiKey) throw new Error("apiKey is required. Get a free key at https://tik.tools");
587
723
  this.apiKey = options.apiKey;
588
724
  this.autoReconnect = options.autoReconnect ?? true;
@@ -596,48 +732,11 @@ var TikTokLive = class extends TypedEmitter {
596
732
  if (!this.WS) {
597
733
  this.WS = await resolveWebSocket(this.webSocketImpl);
598
734
  }
599
- const resp = await fetch(`https://www.tiktok.com/@${this.uniqueId}/live`, {
600
- headers: {
601
- "User-Agent": DEFAULT_UA,
602
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
603
- "Accept-Language": "en-US,en;q=0.9"
604
- },
605
- redirect: "follow"
606
- });
607
- let ttwid = "";
608
- const setCookies = resp.headers.get("set-cookie") || "";
609
- for (const part of setCookies.split(",")) {
610
- const trimmed = part.trim();
611
- if (trimmed.startsWith("ttwid=")) {
612
- ttwid = trimmed.split(";")[0].split("=").slice(1).join("=");
613
- break;
614
- }
615
- }
616
- if (!ttwid && typeof resp.headers.getSetCookie === "function") {
617
- for (const sc of resp.headers.getSetCookie()) {
618
- if (typeof sc === "string" && sc.startsWith("ttwid=")) {
619
- ttwid = sc.split(";")[0].split("=").slice(1).join("=");
620
- break;
621
- }
622
- }
623
- }
624
- if (!ttwid) throw new Error("Failed to obtain session cookie");
625
- const html = await resp.text();
626
- let roomId = "";
627
- const sigiMatch = html.match(/id="SIGI_STATE"[^>]*>([^<]+)/);
628
- if (sigiMatch) {
629
- try {
630
- const json = JSON.parse(sigiMatch[1]);
631
- const jsonStr = JSON.stringify(json);
632
- const m = jsonStr.match(/"roomId"\s*:\s*"(\d+)"/);
633
- if (m) roomId = m[1];
634
- } catch {
635
- }
636
- }
637
- if (!roomId) throw new Error(`User @${this.uniqueId} is not currently live`);
735
+ const pageInfo = await resolveLivePage(this.uniqueId);
736
+ if (!pageInfo) throw new Error(`User @${this.uniqueId} is not currently live`);
737
+ if (!pageInfo.ttwid) throw new Error("Failed to obtain session cookie");
738
+ const { roomId, ttwid, clusterRegion } = pageInfo;
638
739
  this._roomId = roomId;
639
- const crMatch = html.match(/"clusterRegion"\s*:\s*"([^"]+)"/);
640
- const clusterRegion = crMatch ? crMatch[1] : "";
641
740
  const wsHost = getWsHost(clusterRegion);
642
741
  const wsParams = new URLSearchParams({
643
742
  version_code: "270000",
@@ -648,7 +747,7 @@ var TikTokLive = class extends TypedEmitter {
648
747
  browser_language: "en-US",
649
748
  browser_platform: "Win32",
650
749
  browser_name: "Mozilla",
651
- browser_version: DEFAULT_UA.split("Mozilla/")[1] || "5.0",
750
+ browser_version: DEFAULT_UA2.split("Mozilla/")[1] || "5.0",
652
751
  browser_online: "true",
653
752
  tz_name: "Etc/UTC",
654
753
  app_name: "tiktok_web",
@@ -695,7 +794,7 @@ var TikTokLive = class extends TypedEmitter {
695
794
  try {
696
795
  this.ws = new this.WS(connUrl, {
697
796
  headers: {
698
- "User-Agent": DEFAULT_UA,
797
+ "User-Agent": DEFAULT_UA2,
699
798
  "Cookie": `ttwid=${ttwid}`,
700
799
  "Origin": "https://www.tiktok.com"
701
800
  }
@@ -874,6 +973,10 @@ var TikTokLive = class extends TypedEmitter {
874
973
  };
875
974
  // Annotate the CommonJS export names for ESM import in node:
876
975
  0 && (module.exports = {
877
- TikTokLive
976
+ TikTokLive,
977
+ callApi,
978
+ fetchSignedUrl,
979
+ resolveLivePage,
980
+ resolveRoomId
878
981
  });
879
982
  //# sourceMappingURL=index.js.map