tiktok-live-api 1.0.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/LICENSE +21 -0
- package/README.md +290 -0
- package/dist/index.d.mts +349 -0
- package/dist/index.d.ts +349 -0
- package/dist/index.js +291 -0
- package/dist/index.mjs +253 -0
- package/package.json +84 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for TikTok LIVE API events.
|
|
3
|
+
*
|
|
4
|
+
* These types provide IDE autocompletion and type safety
|
|
5
|
+
* when handling events from {@link TikTokLive} and {@link TikTokCaptions}.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/** User profile attached to most LIVE events. */
|
|
10
|
+
interface TikTokUser {
|
|
11
|
+
userId: string;
|
|
12
|
+
uniqueId: string;
|
|
13
|
+
nickname: string;
|
|
14
|
+
profilePictureUrl: string;
|
|
15
|
+
followRole: number;
|
|
16
|
+
isSubscriber: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Payload for `chat` events. */
|
|
19
|
+
interface ChatEvent {
|
|
20
|
+
user: TikTokUser;
|
|
21
|
+
comment: string;
|
|
22
|
+
emotes: Array<{
|
|
23
|
+
emoteId: string;
|
|
24
|
+
image: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
/** Payload for `gift` events. */
|
|
28
|
+
interface GiftEvent {
|
|
29
|
+
user: TikTokUser;
|
|
30
|
+
giftId: number;
|
|
31
|
+
giftName: string;
|
|
32
|
+
diamondCount: number;
|
|
33
|
+
repeatCount: number;
|
|
34
|
+
repeatEnd: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** Payload for `like` events. */
|
|
37
|
+
interface LikeEvent {
|
|
38
|
+
user: TikTokUser;
|
|
39
|
+
likeCount: number;
|
|
40
|
+
totalLikes: number;
|
|
41
|
+
}
|
|
42
|
+
/** Payload for `member` (viewer join) events. */
|
|
43
|
+
interface MemberEvent {
|
|
44
|
+
user: TikTokUser;
|
|
45
|
+
actionId: number;
|
|
46
|
+
}
|
|
47
|
+
/** Payload for `follow` and `share` events. */
|
|
48
|
+
interface SocialEvent {
|
|
49
|
+
user: TikTokUser;
|
|
50
|
+
eventType: string;
|
|
51
|
+
}
|
|
52
|
+
/** Payload for `roomUserSeq` (viewer count) events. */
|
|
53
|
+
interface RoomUserSeqEvent {
|
|
54
|
+
viewerCount: number;
|
|
55
|
+
topViewers: TikTokUser[];
|
|
56
|
+
}
|
|
57
|
+
/** Payload for `battle` events. */
|
|
58
|
+
interface BattleEvent {
|
|
59
|
+
type: string;
|
|
60
|
+
teams: Array<Record<string, unknown>>;
|
|
61
|
+
scores: number[];
|
|
62
|
+
}
|
|
63
|
+
/** Payload for `caption` events from {@link TikTokCaptions}. */
|
|
64
|
+
interface CaptionEvent {
|
|
65
|
+
text: string;
|
|
66
|
+
speaker: string;
|
|
67
|
+
isFinal: boolean;
|
|
68
|
+
language: string;
|
|
69
|
+
}
|
|
70
|
+
/** Payload for `translation` events from {@link TikTokCaptions}. */
|
|
71
|
+
interface TranslationEvent {
|
|
72
|
+
text: string;
|
|
73
|
+
sourceLanguage: string;
|
|
74
|
+
targetLanguage: string;
|
|
75
|
+
}
|
|
76
|
+
/** Payload for `credits` events from {@link TikTokCaptions}. */
|
|
77
|
+
interface CreditsEvent {
|
|
78
|
+
total: number;
|
|
79
|
+
used: number;
|
|
80
|
+
remaining: number;
|
|
81
|
+
}
|
|
82
|
+
/** Connection event payload. */
|
|
83
|
+
interface ConnectedEvent {
|
|
84
|
+
uniqueId: string;
|
|
85
|
+
}
|
|
86
|
+
/** Disconnection event payload. */
|
|
87
|
+
interface DisconnectedEvent {
|
|
88
|
+
uniqueId: string;
|
|
89
|
+
}
|
|
90
|
+
/** Error event payload. */
|
|
91
|
+
interface ErrorEvent {
|
|
92
|
+
error: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Map of event names to their payload types for {@link TikTokLive}.
|
|
96
|
+
*/
|
|
97
|
+
interface TikTokLiveEventMap {
|
|
98
|
+
chat: ChatEvent;
|
|
99
|
+
gift: GiftEvent;
|
|
100
|
+
like: LikeEvent;
|
|
101
|
+
follow: SocialEvent;
|
|
102
|
+
share: SocialEvent;
|
|
103
|
+
member: MemberEvent;
|
|
104
|
+
subscribe: SocialEvent;
|
|
105
|
+
roomUserSeq: RoomUserSeqEvent;
|
|
106
|
+
battle: BattleEvent;
|
|
107
|
+
envelope: Record<string, unknown>;
|
|
108
|
+
streamEnd: Record<string, unknown>;
|
|
109
|
+
roomInfo: Record<string, unknown>;
|
|
110
|
+
connected: ConnectedEvent;
|
|
111
|
+
disconnected: DisconnectedEvent;
|
|
112
|
+
error: ErrorEvent;
|
|
113
|
+
event: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Map of event names to their payload types for {@link TikTokCaptions}.
|
|
117
|
+
*/
|
|
118
|
+
interface TikTokCaptionsEventMap {
|
|
119
|
+
caption: CaptionEvent;
|
|
120
|
+
translation: TranslationEvent;
|
|
121
|
+
credits: CreditsEvent;
|
|
122
|
+
credits_low: CreditsEvent;
|
|
123
|
+
status: Record<string, unknown>;
|
|
124
|
+
connected: ConnectedEvent;
|
|
125
|
+
disconnected: DisconnectedEvent;
|
|
126
|
+
error: ErrorEvent;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* TikTokLive — Connect to any TikTok LIVE stream via WebSocket.
|
|
131
|
+
*
|
|
132
|
+
* Receives real-time events: chat messages, gifts, likes, follows,
|
|
133
|
+
* viewer counts, battles, and more. Powered by the TikTool managed API.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* import { TikTokLive } from 'tiktok-live-api';
|
|
138
|
+
*
|
|
139
|
+
* const client = new TikTokLive('streamer_username', { apiKey: 'YOUR_KEY' });
|
|
140
|
+
*
|
|
141
|
+
* client.on('chat', (event) => {
|
|
142
|
+
* console.log(`${event.user.uniqueId}: ${event.comment}`);
|
|
143
|
+
* });
|
|
144
|
+
*
|
|
145
|
+
* client.connect();
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @packageDocumentation
|
|
149
|
+
*/
|
|
150
|
+
|
|
151
|
+
/** Options for {@link TikTokLive} constructor. */
|
|
152
|
+
interface TikTokLiveOptions {
|
|
153
|
+
/** Your TikTool API key. Get one free at https://tik.tools */
|
|
154
|
+
apiKey?: string;
|
|
155
|
+
/** Auto-reconnect on disconnect (default: true). */
|
|
156
|
+
autoReconnect?: boolean;
|
|
157
|
+
/** Max reconnection attempts (default: 5). */
|
|
158
|
+
maxReconnectAttempts?: number;
|
|
159
|
+
}
|
|
160
|
+
type EventHandler$1<T> = (data: T) => void | Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* Connect to a TikTok LIVE stream and receive real-time events.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* const client = new TikTokLive('username', { apiKey: 'KEY' });
|
|
167
|
+
* client.on('chat', (e) => console.log(e.comment));
|
|
168
|
+
* client.on('gift', (e) => console.log(`${e.giftName} worth ${e.diamondCount} 💎`));
|
|
169
|
+
* client.connect();
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare class TikTokLive {
|
|
173
|
+
/** TikTok username (without @). */
|
|
174
|
+
readonly uniqueId: string;
|
|
175
|
+
/** Your TikTool API key. */
|
|
176
|
+
readonly apiKey: string;
|
|
177
|
+
/** Whether to auto-reconnect on disconnect. */
|
|
178
|
+
readonly autoReconnect: boolean;
|
|
179
|
+
/** Maximum reconnection attempts. */
|
|
180
|
+
readonly maxReconnectAttempts: number;
|
|
181
|
+
private _handlers;
|
|
182
|
+
private _ws;
|
|
183
|
+
private _connected;
|
|
184
|
+
private _intentionalClose;
|
|
185
|
+
private _reconnectAttempts;
|
|
186
|
+
private _eventCount;
|
|
187
|
+
/**
|
|
188
|
+
* Create a new TikTokLive client.
|
|
189
|
+
*
|
|
190
|
+
* @param uniqueId - TikTok username (without @)
|
|
191
|
+
* @param options - Configuration options
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* const client = new TikTokLive('streamer', { apiKey: 'YOUR_KEY' });
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
constructor(uniqueId: string, options?: TikTokLiveOptions);
|
|
199
|
+
/** Whether the client is currently connected. */
|
|
200
|
+
get connected(): boolean;
|
|
201
|
+
/** Total number of events received this session. */
|
|
202
|
+
get eventCount(): number;
|
|
203
|
+
/**
|
|
204
|
+
* Register an event handler.
|
|
205
|
+
*
|
|
206
|
+
* @param event - Event name (chat, gift, like, follow, etc.)
|
|
207
|
+
* @param handler - Callback function
|
|
208
|
+
* @returns this (for chaining)
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* client.on('chat', (event) => console.log(event.comment));
|
|
213
|
+
* client.on('gift', (event) => console.log(event.giftName));
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
on<K extends keyof TikTokLiveEventMap>(event: K, handler: EventHandler$1<TikTokLiveEventMap[K]>): this;
|
|
217
|
+
on(event: string, handler: EventHandler$1<any>): this;
|
|
218
|
+
/**
|
|
219
|
+
* Remove an event handler.
|
|
220
|
+
*
|
|
221
|
+
* @param event - Event name
|
|
222
|
+
* @param handler - The handler to remove
|
|
223
|
+
*/
|
|
224
|
+
off(event: string, handler: EventHandler$1<any>): this;
|
|
225
|
+
private _emit;
|
|
226
|
+
/**
|
|
227
|
+
* Connect to the TikTok LIVE stream.
|
|
228
|
+
*
|
|
229
|
+
* @returns Promise that resolves when connected, rejects on fatal error.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```typescript
|
|
233
|
+
* await client.connect();
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
connect(): Promise<void>;
|
|
237
|
+
/**
|
|
238
|
+
* Disconnect from the stream.
|
|
239
|
+
*/
|
|
240
|
+
disconnect(): void;
|
|
241
|
+
private _maybeReconnect;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* TikTokCaptions — Real-time AI speech-to-text for TikTok LIVE streams.
|
|
246
|
+
*
|
|
247
|
+
* Transcribe and translate any TikTok LIVE stream in real-time.
|
|
248
|
+
* This feature is unique to TikTool Live — no other service offers it.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```typescript
|
|
252
|
+
* import { TikTokCaptions } from 'tiktok-live-api';
|
|
253
|
+
*
|
|
254
|
+
* const captions = new TikTokCaptions('streamer', {
|
|
255
|
+
* apiKey: 'YOUR_KEY',
|
|
256
|
+
* translate: 'en',
|
|
257
|
+
* diarization: true,
|
|
258
|
+
* });
|
|
259
|
+
*
|
|
260
|
+
* captions.on('caption', (event) => {
|
|
261
|
+
* console.log(`[${event.speaker}] ${event.text}`);
|
|
262
|
+
* });
|
|
263
|
+
*
|
|
264
|
+
* captions.connect();
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* @packageDocumentation
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
/** Options for {@link TikTokCaptions} constructor. */
|
|
271
|
+
interface TikTokCaptionsOptions {
|
|
272
|
+
/** Your TikTool API key. Get one at https://tik.tools */
|
|
273
|
+
apiKey?: string;
|
|
274
|
+
/** Target language code for real-time translation (e.g. "en", "es"). */
|
|
275
|
+
translate?: string;
|
|
276
|
+
/** Enable speaker identification (default: true). */
|
|
277
|
+
diarization?: boolean;
|
|
278
|
+
/** Auto-disconnect after N minutes (default: 60, max: 300). */
|
|
279
|
+
maxDurationMinutes?: number;
|
|
280
|
+
}
|
|
281
|
+
type EventHandler<T> = (data: T) => void | Promise<void>;
|
|
282
|
+
/**
|
|
283
|
+
* Real-time AI speech-to-text for TikTok LIVE streams.
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* const captions = new TikTokCaptions('streamer', {
|
|
288
|
+
* apiKey: 'KEY',
|
|
289
|
+
* translate: 'en',
|
|
290
|
+
* });
|
|
291
|
+
* captions.on('caption', (e) => console.log(e.text));
|
|
292
|
+
* captions.on('translation', (e) => console.log(`→ ${e.text}`));
|
|
293
|
+
* captions.connect();
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
declare class TikTokCaptions {
|
|
297
|
+
/** TikTok username (without @). */
|
|
298
|
+
readonly uniqueId: string;
|
|
299
|
+
/** Your TikTool API key. */
|
|
300
|
+
readonly apiKey: string;
|
|
301
|
+
/** Target translation language. */
|
|
302
|
+
readonly translate?: string;
|
|
303
|
+
/** Whether speaker diarization is enabled. */
|
|
304
|
+
readonly diarization: boolean;
|
|
305
|
+
/** Max session duration in minutes. */
|
|
306
|
+
readonly maxDurationMinutes?: number;
|
|
307
|
+
private _handlers;
|
|
308
|
+
private _ws;
|
|
309
|
+
private _connected;
|
|
310
|
+
private _intentionalClose;
|
|
311
|
+
/**
|
|
312
|
+
* Create a new TikTokCaptions client.
|
|
313
|
+
*
|
|
314
|
+
* @param uniqueId - TikTok username (without @)
|
|
315
|
+
* @param options - Configuration options
|
|
316
|
+
*/
|
|
317
|
+
constructor(uniqueId: string, options?: TikTokCaptionsOptions);
|
|
318
|
+
/** Whether currently connected and receiving captions. */
|
|
319
|
+
get connected(): boolean;
|
|
320
|
+
/**
|
|
321
|
+
* Register an event handler.
|
|
322
|
+
*
|
|
323
|
+
* @param event - Event name (caption, translation, credits, status, error)
|
|
324
|
+
* @param handler - Callback function
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* captions.on('caption', (event) => {
|
|
329
|
+
* const prefix = event.speaker ? `[${event.speaker}] ` : '';
|
|
330
|
+
* console.log(`${prefix}${event.text}${event.isFinal ? ' ✓' : '...'}`);
|
|
331
|
+
* });
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
on<K extends keyof TikTokCaptionsEventMap>(event: K, handler: EventHandler<TikTokCaptionsEventMap[K]>): this;
|
|
335
|
+
on(event: string, handler: EventHandler<any>): this;
|
|
336
|
+
/** Remove an event handler. */
|
|
337
|
+
off(event: string, handler: EventHandler<any>): this;
|
|
338
|
+
private _emit;
|
|
339
|
+
/**
|
|
340
|
+
* Start receiving captions from the stream.
|
|
341
|
+
*
|
|
342
|
+
* @returns Promise that resolves when connected.
|
|
343
|
+
*/
|
|
344
|
+
connect(): Promise<void>;
|
|
345
|
+
/** Stop receiving captions. */
|
|
346
|
+
disconnect(): void;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export { type BattleEvent, type CaptionEvent, type ChatEvent, type ConnectedEvent, type CreditsEvent, type DisconnectedEvent, type ErrorEvent, type GiftEvent, type LikeEvent, type MemberEvent, type RoomUserSeqEvent, type SocialEvent, TikTokCaptions, type TikTokCaptionsEventMap, type TikTokCaptionsOptions, TikTokLive, type TikTokLiveEventMap, type TikTokLiveOptions, type TikTokUser, type TranslationEvent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
TikTokCaptions: () => TikTokCaptions,
|
|
34
|
+
TikTokLive: () => TikTokLive
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/client.ts
|
|
39
|
+
var import_ws = __toESM(require("ws"));
|
|
40
|
+
var WS_BASE = "wss://api.tik.tools";
|
|
41
|
+
var VERSION = "1.0.0";
|
|
42
|
+
var TikTokLive = class {
|
|
43
|
+
/**
|
|
44
|
+
* Create a new TikTokLive client.
|
|
45
|
+
*
|
|
46
|
+
* @param uniqueId - TikTok username (without @)
|
|
47
|
+
* @param options - Configuration options
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const client = new TikTokLive('streamer', { apiKey: 'YOUR_KEY' });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
constructor(uniqueId, options = {}) {
|
|
55
|
+
this._handlers = /* @__PURE__ */ new Map();
|
|
56
|
+
this._ws = null;
|
|
57
|
+
this._connected = false;
|
|
58
|
+
this._intentionalClose = false;
|
|
59
|
+
this._reconnectAttempts = 0;
|
|
60
|
+
this._eventCount = 0;
|
|
61
|
+
this.uniqueId = uniqueId.replace(/^@/, "");
|
|
62
|
+
this.apiKey = options.apiKey || process.env.TIKTOOL_API_KEY || "";
|
|
63
|
+
if (!this.apiKey) {
|
|
64
|
+
throw new Error("apiKey is required. Get a free key at https://tik.tools");
|
|
65
|
+
}
|
|
66
|
+
this.autoReconnect = options.autoReconnect ?? true;
|
|
67
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
|
|
68
|
+
}
|
|
69
|
+
/** Whether the client is currently connected. */
|
|
70
|
+
get connected() {
|
|
71
|
+
return this._connected;
|
|
72
|
+
}
|
|
73
|
+
/** Total number of events received this session. */
|
|
74
|
+
get eventCount() {
|
|
75
|
+
return this._eventCount;
|
|
76
|
+
}
|
|
77
|
+
on(event, handler) {
|
|
78
|
+
if (!this._handlers.has(event)) {
|
|
79
|
+
this._handlers.set(event, /* @__PURE__ */ new Set());
|
|
80
|
+
}
|
|
81
|
+
this._handlers.get(event).add(handler);
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove an event handler.
|
|
86
|
+
*
|
|
87
|
+
* @param event - Event name
|
|
88
|
+
* @param handler - The handler to remove
|
|
89
|
+
*/
|
|
90
|
+
off(event, handler) {
|
|
91
|
+
this._handlers.get(event)?.delete(handler);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
_emit(event, data) {
|
|
95
|
+
const handlers = this._handlers.get(event);
|
|
96
|
+
if (!handlers) return;
|
|
97
|
+
for (const handler of handlers) {
|
|
98
|
+
try {
|
|
99
|
+
const result = handler(data);
|
|
100
|
+
if (result instanceof Promise) {
|
|
101
|
+
result.catch(
|
|
102
|
+
(err) => console.error(`Error in '${event}' handler:`, err)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error(`Error in '${event}' handler:`, err);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Connect to the TikTok LIVE stream.
|
|
112
|
+
*
|
|
113
|
+
* @returns Promise that resolves when connected, rejects on fatal error.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* await client.connect();
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
async connect() {
|
|
121
|
+
this._intentionalClose = false;
|
|
122
|
+
const uri = `${WS_BASE}?uniqueId=${this.uniqueId}&apiKey=${this.apiKey}`;
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
this._ws = new import_ws.default(uri, {
|
|
125
|
+
headers: { "User-Agent": `tiktok-live-api/${VERSION}` }
|
|
126
|
+
});
|
|
127
|
+
this._ws.on("open", () => {
|
|
128
|
+
this._connected = true;
|
|
129
|
+
this._reconnectAttempts = 0;
|
|
130
|
+
this._emit("connected", { uniqueId: this.uniqueId });
|
|
131
|
+
resolve();
|
|
132
|
+
});
|
|
133
|
+
this._ws.on("message", (raw) => {
|
|
134
|
+
try {
|
|
135
|
+
const event = JSON.parse(raw.toString());
|
|
136
|
+
this._eventCount++;
|
|
137
|
+
const eventType = event.event || "unknown";
|
|
138
|
+
const data = event.data || event;
|
|
139
|
+
this._emit("event", event);
|
|
140
|
+
this._emit(eventType, data);
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
this._ws.on("close", () => {
|
|
145
|
+
this._connected = false;
|
|
146
|
+
this._emit("disconnected", { uniqueId: this.uniqueId });
|
|
147
|
+
this._maybeReconnect();
|
|
148
|
+
});
|
|
149
|
+
this._ws.on("error", (err) => {
|
|
150
|
+
this._emit("error", { error: err.message });
|
|
151
|
+
if (!this._connected) reject(err);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Disconnect from the stream.
|
|
157
|
+
*/
|
|
158
|
+
disconnect() {
|
|
159
|
+
this._intentionalClose = true;
|
|
160
|
+
if (this._ws) {
|
|
161
|
+
this._ws.close();
|
|
162
|
+
this._ws = null;
|
|
163
|
+
}
|
|
164
|
+
this._connected = false;
|
|
165
|
+
}
|
|
166
|
+
async _maybeReconnect() {
|
|
167
|
+
if (this._intentionalClose || !this.autoReconnect || this._reconnectAttempts >= this.maxReconnectAttempts) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this._reconnectAttempts++;
|
|
171
|
+
const delay = Math.min(2 ** (this._reconnectAttempts - 1) * 1e3, 3e4);
|
|
172
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
173
|
+
try {
|
|
174
|
+
await this.connect();
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// src/captions.ts
|
|
181
|
+
var import_ws2 = __toESM(require("ws"));
|
|
182
|
+
var CAPTIONS_BASE = "wss://api.tik.tools/captions";
|
|
183
|
+
var VERSION2 = "1.0.0";
|
|
184
|
+
var TikTokCaptions = class {
|
|
185
|
+
/**
|
|
186
|
+
* Create a new TikTokCaptions client.
|
|
187
|
+
*
|
|
188
|
+
* @param uniqueId - TikTok username (without @)
|
|
189
|
+
* @param options - Configuration options
|
|
190
|
+
*/
|
|
191
|
+
constructor(uniqueId, options = {}) {
|
|
192
|
+
this._handlers = /* @__PURE__ */ new Map();
|
|
193
|
+
this._ws = null;
|
|
194
|
+
this._connected = false;
|
|
195
|
+
this._intentionalClose = false;
|
|
196
|
+
this.uniqueId = uniqueId.replace(/^@/, "");
|
|
197
|
+
this.apiKey = options.apiKey || process.env.TIKTOOL_API_KEY || "";
|
|
198
|
+
if (!this.apiKey) {
|
|
199
|
+
throw new Error("apiKey is required. Get a free key at https://tik.tools");
|
|
200
|
+
}
|
|
201
|
+
this.translate = options.translate;
|
|
202
|
+
this.diarization = options.diarization ?? true;
|
|
203
|
+
this.maxDurationMinutes = options.maxDurationMinutes;
|
|
204
|
+
}
|
|
205
|
+
/** Whether currently connected and receiving captions. */
|
|
206
|
+
get connected() {
|
|
207
|
+
return this._connected;
|
|
208
|
+
}
|
|
209
|
+
on(event, handler) {
|
|
210
|
+
if (!this._handlers.has(event)) {
|
|
211
|
+
this._handlers.set(event, /* @__PURE__ */ new Set());
|
|
212
|
+
}
|
|
213
|
+
this._handlers.get(event).add(handler);
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
/** Remove an event handler. */
|
|
217
|
+
off(event, handler) {
|
|
218
|
+
this._handlers.get(event)?.delete(handler);
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
_emit(event, data) {
|
|
222
|
+
const handlers = this._handlers.get(event);
|
|
223
|
+
if (!handlers) return;
|
|
224
|
+
for (const handler of handlers) {
|
|
225
|
+
try {
|
|
226
|
+
const result = handler(data);
|
|
227
|
+
if (result instanceof Promise) {
|
|
228
|
+
result.catch(
|
|
229
|
+
(err) => console.error(`Error in '${event}' handler:`, err)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.error(`Error in '${event}' handler:`, err);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Start receiving captions from the stream.
|
|
239
|
+
*
|
|
240
|
+
* @returns Promise that resolves when connected.
|
|
241
|
+
*/
|
|
242
|
+
async connect() {
|
|
243
|
+
this._intentionalClose = false;
|
|
244
|
+
let params = `uniqueId=${this.uniqueId}&apiKey=${this.apiKey}`;
|
|
245
|
+
if (this.translate) params += `&translate=${this.translate}`;
|
|
246
|
+
if (this.diarization) params += "&diarization=true";
|
|
247
|
+
if (this.maxDurationMinutes)
|
|
248
|
+
params += `&max_duration_minutes=${this.maxDurationMinutes}`;
|
|
249
|
+
const uri = `${CAPTIONS_BASE}?${params}`;
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
this._ws = new import_ws2.default(uri, {
|
|
252
|
+
headers: { "User-Agent": `tiktok-live-api/${VERSION2}` }
|
|
253
|
+
});
|
|
254
|
+
this._ws.on("open", () => {
|
|
255
|
+
this._connected = true;
|
|
256
|
+
this._emit("connected", { uniqueId: this.uniqueId });
|
|
257
|
+
resolve();
|
|
258
|
+
});
|
|
259
|
+
this._ws.on("message", (raw) => {
|
|
260
|
+
try {
|
|
261
|
+
const event = JSON.parse(raw.toString());
|
|
262
|
+
const msgType = event.type || "unknown";
|
|
263
|
+
this._emit(msgType, event);
|
|
264
|
+
} catch {
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
this._ws.on("close", () => {
|
|
268
|
+
this._connected = false;
|
|
269
|
+
this._emit("disconnected", { uniqueId: this.uniqueId });
|
|
270
|
+
});
|
|
271
|
+
this._ws.on("error", (err) => {
|
|
272
|
+
this._emit("error", { error: err.message });
|
|
273
|
+
if (!this._connected) reject(err);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
/** Stop receiving captions. */
|
|
278
|
+
disconnect() {
|
|
279
|
+
this._intentionalClose = true;
|
|
280
|
+
if (this._ws) {
|
|
281
|
+
this._ws.close();
|
|
282
|
+
this._ws = null;
|
|
283
|
+
}
|
|
284
|
+
this._connected = false;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
288
|
+
0 && (module.exports = {
|
|
289
|
+
TikTokCaptions,
|
|
290
|
+
TikTokLive
|
|
291
|
+
});
|