@tiktool/live 2.2.0 → 2.3.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/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
 
@@ -25,7 +25,7 @@
25
25
  - **Universal runtime support** — Node.js 18+, Bun, Deno, Cloudflare Workers, Durable Objects, and browsers
26
26
  - **Full TypeScript** — complete type definitions for all 18+ event types with IDE autocompletion
27
27
  - **Battle-tested** — production-ready with auto-reconnect, heartbeat management, and error recovery
28
- - **Generous free tier** — 30 requests/min, 3 concurrent WebSocket connections, all endpoints included
28
+ - **Generous free tier** — 2,500 requests/day, 1 WebSocket connection, no credit card required
29
29
 
30
30
  ---
31
31
 
@@ -35,7 +35,7 @@
35
35
  npm install @tiktool/live
36
36
  ```
37
37
 
38
- Get your free API key at **[tik.tools](https://tik.tools)** — all endpoints are accessible on the free tier.
38
+ Get your free API key at **[tik.tools](https://tik.tools)** — no credit card required. The free tier includes WebSocket signing, live status checks, and 2,500 requests/day.
39
39
 
40
40
  ```typescript
41
41
  import { TikTokLive } from '@tiktool/live';
@@ -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. All endpoints are accessible on every tier higher tiers unlock increased rate limits and concurrent connections.
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
  |----------|-------------|
@@ -155,17 +248,21 @@ Full API documentation: **[tik.tools/docs](https://tik.tools/docs)**
155
248
 
156
249
  ## 💰 Pricing
157
250
 
158
- All plans include access to **every endpoint**. Higher tiers unlock increased throughput.
251
+ All plans include signatures and full WebSocket support. Higher tiers unlock all endpoints, priority routing, and increased throughput.
159
252
 
160
253
  | | **Free** | **Pro** | **Ultra** |
161
254
  |---|---|---|---|
162
- | **Price** | Free forever | $29/mo | $99/mo |
163
- | **Rate Limit** | 30 req/min | 120 req/min | 300 req/min |
164
- | **WebSocket Connections** | 3 concurrent | 50 concurrent | 500 concurrent |
165
- | **Bulk Live Check** | 5 users/req | 50 users/req | 100 users/req |
166
- | **Support** | Community | Priority | Dedicated |
167
-
168
- Get your API key at **[tik.tools](https://tik.tools)**.
255
+ | **Weekly** | Free | $15/wk | $45/wk |
256
+ | **Monthly** | Free | $48/mo | $169/mo |
257
+ | **Yearly** | Free | $39/mo | $149/mo |
258
+ | **Daily Requests** | 2,500 | 50,000 | 250,000 |
259
+ | **WebSocket Connections** | 1 | 50 | 500 |
260
+ | **Endpoints** | Basic | All | All |
261
+ | **Priority Routing** | | ✓ | ✓ |
262
+ | **Overage** | — | $0.001/req | $0.0005/req |
263
+ | **Support** | Community | Email | Dedicated |
264
+
265
+ Get your Free API key at **[tik.tools](https://tik.tools)**.
169
266
 
170
267
  ---
171
268
 
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
 
@@ -495,9 +499,140 @@ function parseWebcastResponse(payload) {
495
499
  return events;
496
500
  }
497
501
 
498
- // src/client.ts
502
+ // src/api.ts
499
503
  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
504
  var DEFAULT_SIGN_SERVER = "https://api.tik.tools";
505
+ var pageCache = /* @__PURE__ */ new Map();
506
+ var PAGE_CACHE_TTL = 5 * 60 * 1e3;
507
+ async function resolveLivePage(uniqueId) {
508
+ const clean = uniqueId.replace(/^@/, "");
509
+ const cached = pageCache.get(clean);
510
+ if (cached && Date.now() - cached.ts < PAGE_CACHE_TTL) {
511
+ return cached.info;
512
+ }
513
+ try {
514
+ const resp = await fetch(`https://www.tiktok.com/@${clean}/live`, {
515
+ headers: {
516
+ "User-Agent": DEFAULT_UA,
517
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
518
+ "Accept-Language": "en-US,en;q=0.9"
519
+ },
520
+ redirect: "follow"
521
+ });
522
+ if (!resp.ok) return null;
523
+ let ttwid = "";
524
+ const setCookies = resp.headers.get("set-cookie") || "";
525
+ for (const part of setCookies.split(",")) {
526
+ const trimmed = part.trim();
527
+ if (trimmed.startsWith("ttwid=")) {
528
+ ttwid = trimmed.split(";")[0].split("=").slice(1).join("=");
529
+ break;
530
+ }
531
+ }
532
+ if (!ttwid && typeof resp.headers.getSetCookie === "function") {
533
+ for (const sc of resp.headers.getSetCookie()) {
534
+ if (typeof sc === "string" && sc.startsWith("ttwid=")) {
535
+ ttwid = sc.split(";")[0].split("=").slice(1).join("=");
536
+ break;
537
+ }
538
+ }
539
+ }
540
+ const html = await resp.text();
541
+ let roomId = "";
542
+ const sigiMatch = html.match(/id="SIGI_STATE"[^>]*>([^<]+)/);
543
+ if (sigiMatch) {
544
+ try {
545
+ const json = JSON.parse(sigiMatch[1]);
546
+ const jsonStr = JSON.stringify(json);
547
+ const m = jsonStr.match(/"roomId"\s*:\s*"(\d+)"/);
548
+ if (m) roomId = m[1];
549
+ } catch {
550
+ }
551
+ }
552
+ if (!roomId) {
553
+ const patterns = [
554
+ /"roomId"\s*:\s*"(\d+)"/,
555
+ /room_id[=/](\d{10,})/,
556
+ /"idStr"\s*:\s*"(\d{10,})"/
557
+ ];
558
+ for (const p of patterns) {
559
+ const m = html.match(p);
560
+ if (m) {
561
+ roomId = m[1];
562
+ break;
563
+ }
564
+ }
565
+ }
566
+ if (!roomId) return null;
567
+ const crMatch = html.match(/"clusterRegion"\s*:\s*"([^"]+)"/);
568
+ const clusterRegion = crMatch ? crMatch[1] : "";
569
+ const info = { roomId, ttwid, clusterRegion };
570
+ pageCache.set(clean, { info, ts: Date.now() });
571
+ return info;
572
+ } catch {
573
+ }
574
+ return null;
575
+ }
576
+ async function resolveRoomId(uniqueId) {
577
+ const info = await resolveLivePage(uniqueId);
578
+ return info?.roomId ?? null;
579
+ }
580
+ async function fetchSignedUrl(response) {
581
+ if (response.action !== "fetch_signed_url" || !response.signed_url) {
582
+ return null;
583
+ }
584
+ const headers = { ...response.headers || {} };
585
+ if (response.cookies) {
586
+ headers["Cookie"] = response.cookies;
587
+ }
588
+ const resp = await fetch(response.signed_url, { headers, redirect: "follow" });
589
+ const text = await resp.text();
590
+ try {
591
+ return JSON.parse(text);
592
+ } catch {
593
+ return null;
594
+ }
595
+ }
596
+ async function callApi(opts) {
597
+ const serverUrl = (opts.serverUrl || DEFAULT_SIGN_SERVER).replace(/\/$/, "");
598
+ const isGet = opts.method === "GET";
599
+ const ak = encodeURIComponent(opts.apiKey);
600
+ const url1 = isGet ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&unique_id=${encodeURIComponent(opts.uniqueId)}` : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;
601
+ const fetchOpts1 = isGet ? {} : {
602
+ method: "POST",
603
+ headers: { "Content-Type": "application/json" },
604
+ body: JSON.stringify({ unique_id: opts.uniqueId, ...opts.extraBody })
605
+ };
606
+ const resp1 = await fetch(url1, fetchOpts1);
607
+ const data1 = await resp1.json();
608
+ if (data1.status_code === 0 && !["resolve_required", "fetch_signed_url"].includes(data1.action)) {
609
+ return data1;
610
+ }
611
+ if (data1.action === "fetch_signed_url") {
612
+ return fetchSignedUrl(data1);
613
+ }
614
+ if (data1.action === "resolve_required") {
615
+ const roomId = await resolveRoomId(opts.uniqueId);
616
+ if (!roomId) return null;
617
+ const url2 = isGet ? `${serverUrl}${opts.endpoint}?apiKey=${ak}&room_id=${encodeURIComponent(roomId)}` : `${serverUrl}${opts.endpoint}?apiKey=${ak}`;
618
+ const fetchOpts2 = isGet ? {} : {
619
+ method: "POST",
620
+ headers: { "Content-Type": "application/json" },
621
+ body: JSON.stringify({ room_id: roomId, ...opts.extraBody })
622
+ };
623
+ const resp2 = await fetch(url2, fetchOpts2);
624
+ const data2 = await resp2.json();
625
+ if (data2.action === "fetch_signed_url") {
626
+ return fetchSignedUrl(data2);
627
+ }
628
+ return data2;
629
+ }
630
+ return data1;
631
+ }
632
+
633
+ // src/client.ts
634
+ 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";
635
+ var DEFAULT_SIGN_SERVER2 = "https://api.tik.tools";
501
636
  var TypedEmitter = class {
502
637
  _listeners = /* @__PURE__ */ new Map();
503
638
  on(event, fn) {
@@ -582,7 +717,7 @@ var TikTokLive = class extends TypedEmitter {
582
717
  constructor(options) {
583
718
  super();
584
719
  this.uniqueId = options.uniqueId.replace(/^@/, "");
585
- this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER).replace(/\/$/, "");
720
+ this.signServerUrl = (options.signServerUrl || DEFAULT_SIGN_SERVER2).replace(/\/$/, "");
586
721
  if (!options.apiKey) throw new Error("apiKey is required. Get a free key at https://tik.tools");
587
722
  this.apiKey = options.apiKey;
588
723
  this.autoReconnect = options.autoReconnect ?? true;
@@ -596,48 +731,11 @@ var TikTokLive = class extends TypedEmitter {
596
731
  if (!this.WS) {
597
732
  this.WS = await resolveWebSocket(this.webSocketImpl);
598
733
  }
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`);
734
+ const pageInfo = await resolveLivePage(this.uniqueId);
735
+ if (!pageInfo) throw new Error(`User @${this.uniqueId} is not currently live`);
736
+ if (!pageInfo.ttwid) throw new Error("Failed to obtain session cookie");
737
+ const { roomId, ttwid, clusterRegion } = pageInfo;
638
738
  this._roomId = roomId;
639
- const crMatch = html.match(/"clusterRegion"\s*:\s*"([^"]+)"/);
640
- const clusterRegion = crMatch ? crMatch[1] : "";
641
739
  const wsHost = getWsHost(clusterRegion);
642
740
  const wsParams = new URLSearchParams({
643
741
  version_code: "270000",
@@ -648,7 +746,7 @@ var TikTokLive = class extends TypedEmitter {
648
746
  browser_language: "en-US",
649
747
  browser_platform: "Win32",
650
748
  browser_name: "Mozilla",
651
- browser_version: DEFAULT_UA.split("Mozilla/")[1] || "5.0",
749
+ browser_version: DEFAULT_UA2.split("Mozilla/")[1] || "5.0",
652
750
  browser_online: "true",
653
751
  tz_name: "Etc/UTC",
654
752
  app_name: "tiktok_web",
@@ -695,7 +793,7 @@ var TikTokLive = class extends TypedEmitter {
695
793
  try {
696
794
  this.ws = new this.WS(connUrl, {
697
795
  headers: {
698
- "User-Agent": DEFAULT_UA,
796
+ "User-Agent": DEFAULT_UA2,
699
797
  "Cookie": `ttwid=${ttwid}`,
700
798
  "Origin": "https://www.tiktok.com"
701
799
  }
@@ -874,6 +972,10 @@ var TikTokLive = class extends TypedEmitter {
874
972
  };
875
973
  // Annotate the CommonJS export names for ESM import in node:
876
974
  0 && (module.exports = {
877
- TikTokLive
975
+ TikTokLive,
976
+ callApi,
977
+ fetchSignedUrl,
978
+ resolveLivePage,
979
+ resolveRoomId
878
980
  });
879
981
  //# sourceMappingURL=index.js.map