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.mjs
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import WebSocket from "ws";
|
|
3
|
+
var WS_BASE = "wss://api.tik.tools";
|
|
4
|
+
var VERSION = "1.0.0";
|
|
5
|
+
var TikTokLive = class {
|
|
6
|
+
/**
|
|
7
|
+
* Create a new TikTokLive client.
|
|
8
|
+
*
|
|
9
|
+
* @param uniqueId - TikTok username (without @)
|
|
10
|
+
* @param options - Configuration options
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const client = new TikTokLive('streamer', { apiKey: 'YOUR_KEY' });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
constructor(uniqueId, options = {}) {
|
|
18
|
+
this._handlers = /* @__PURE__ */ new Map();
|
|
19
|
+
this._ws = null;
|
|
20
|
+
this._connected = false;
|
|
21
|
+
this._intentionalClose = false;
|
|
22
|
+
this._reconnectAttempts = 0;
|
|
23
|
+
this._eventCount = 0;
|
|
24
|
+
this.uniqueId = uniqueId.replace(/^@/, "");
|
|
25
|
+
this.apiKey = options.apiKey || process.env.TIKTOOL_API_KEY || "";
|
|
26
|
+
if (!this.apiKey) {
|
|
27
|
+
throw new Error("apiKey is required. Get a free key at https://tik.tools");
|
|
28
|
+
}
|
|
29
|
+
this.autoReconnect = options.autoReconnect ?? true;
|
|
30
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
|
|
31
|
+
}
|
|
32
|
+
/** Whether the client is currently connected. */
|
|
33
|
+
get connected() {
|
|
34
|
+
return this._connected;
|
|
35
|
+
}
|
|
36
|
+
/** Total number of events received this session. */
|
|
37
|
+
get eventCount() {
|
|
38
|
+
return this._eventCount;
|
|
39
|
+
}
|
|
40
|
+
on(event, handler) {
|
|
41
|
+
if (!this._handlers.has(event)) {
|
|
42
|
+
this._handlers.set(event, /* @__PURE__ */ new Set());
|
|
43
|
+
}
|
|
44
|
+
this._handlers.get(event).add(handler);
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Remove an event handler.
|
|
49
|
+
*
|
|
50
|
+
* @param event - Event name
|
|
51
|
+
* @param handler - The handler to remove
|
|
52
|
+
*/
|
|
53
|
+
off(event, handler) {
|
|
54
|
+
this._handlers.get(event)?.delete(handler);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
_emit(event, data) {
|
|
58
|
+
const handlers = this._handlers.get(event);
|
|
59
|
+
if (!handlers) return;
|
|
60
|
+
for (const handler of handlers) {
|
|
61
|
+
try {
|
|
62
|
+
const result = handler(data);
|
|
63
|
+
if (result instanceof Promise) {
|
|
64
|
+
result.catch(
|
|
65
|
+
(err) => console.error(`Error in '${event}' handler:`, err)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error(`Error in '${event}' handler:`, err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Connect to the TikTok LIVE stream.
|
|
75
|
+
*
|
|
76
|
+
* @returns Promise that resolves when connected, rejects on fatal error.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* await client.connect();
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
async connect() {
|
|
84
|
+
this._intentionalClose = false;
|
|
85
|
+
const uri = `${WS_BASE}?uniqueId=${this.uniqueId}&apiKey=${this.apiKey}`;
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
this._ws = new WebSocket(uri, {
|
|
88
|
+
headers: { "User-Agent": `tiktok-live-api/${VERSION}` }
|
|
89
|
+
});
|
|
90
|
+
this._ws.on("open", () => {
|
|
91
|
+
this._connected = true;
|
|
92
|
+
this._reconnectAttempts = 0;
|
|
93
|
+
this._emit("connected", { uniqueId: this.uniqueId });
|
|
94
|
+
resolve();
|
|
95
|
+
});
|
|
96
|
+
this._ws.on("message", (raw) => {
|
|
97
|
+
try {
|
|
98
|
+
const event = JSON.parse(raw.toString());
|
|
99
|
+
this._eventCount++;
|
|
100
|
+
const eventType = event.event || "unknown";
|
|
101
|
+
const data = event.data || event;
|
|
102
|
+
this._emit("event", event);
|
|
103
|
+
this._emit(eventType, data);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
this._ws.on("close", () => {
|
|
108
|
+
this._connected = false;
|
|
109
|
+
this._emit("disconnected", { uniqueId: this.uniqueId });
|
|
110
|
+
this._maybeReconnect();
|
|
111
|
+
});
|
|
112
|
+
this._ws.on("error", (err) => {
|
|
113
|
+
this._emit("error", { error: err.message });
|
|
114
|
+
if (!this._connected) reject(err);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Disconnect from the stream.
|
|
120
|
+
*/
|
|
121
|
+
disconnect() {
|
|
122
|
+
this._intentionalClose = true;
|
|
123
|
+
if (this._ws) {
|
|
124
|
+
this._ws.close();
|
|
125
|
+
this._ws = null;
|
|
126
|
+
}
|
|
127
|
+
this._connected = false;
|
|
128
|
+
}
|
|
129
|
+
async _maybeReconnect() {
|
|
130
|
+
if (this._intentionalClose || !this.autoReconnect || this._reconnectAttempts >= this.maxReconnectAttempts) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this._reconnectAttempts++;
|
|
134
|
+
const delay = Math.min(2 ** (this._reconnectAttempts - 1) * 1e3, 3e4);
|
|
135
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
136
|
+
try {
|
|
137
|
+
await this.connect();
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/captions.ts
|
|
144
|
+
import WebSocket2 from "ws";
|
|
145
|
+
var CAPTIONS_BASE = "wss://api.tik.tools/captions";
|
|
146
|
+
var VERSION2 = "1.0.0";
|
|
147
|
+
var TikTokCaptions = class {
|
|
148
|
+
/**
|
|
149
|
+
* Create a new TikTokCaptions client.
|
|
150
|
+
*
|
|
151
|
+
* @param uniqueId - TikTok username (without @)
|
|
152
|
+
* @param options - Configuration options
|
|
153
|
+
*/
|
|
154
|
+
constructor(uniqueId, options = {}) {
|
|
155
|
+
this._handlers = /* @__PURE__ */ new Map();
|
|
156
|
+
this._ws = null;
|
|
157
|
+
this._connected = false;
|
|
158
|
+
this._intentionalClose = false;
|
|
159
|
+
this.uniqueId = uniqueId.replace(/^@/, "");
|
|
160
|
+
this.apiKey = options.apiKey || process.env.TIKTOOL_API_KEY || "";
|
|
161
|
+
if (!this.apiKey) {
|
|
162
|
+
throw new Error("apiKey is required. Get a free key at https://tik.tools");
|
|
163
|
+
}
|
|
164
|
+
this.translate = options.translate;
|
|
165
|
+
this.diarization = options.diarization ?? true;
|
|
166
|
+
this.maxDurationMinutes = options.maxDurationMinutes;
|
|
167
|
+
}
|
|
168
|
+
/** Whether currently connected and receiving captions. */
|
|
169
|
+
get connected() {
|
|
170
|
+
return this._connected;
|
|
171
|
+
}
|
|
172
|
+
on(event, handler) {
|
|
173
|
+
if (!this._handlers.has(event)) {
|
|
174
|
+
this._handlers.set(event, /* @__PURE__ */ new Set());
|
|
175
|
+
}
|
|
176
|
+
this._handlers.get(event).add(handler);
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
/** Remove an event handler. */
|
|
180
|
+
off(event, handler) {
|
|
181
|
+
this._handlers.get(event)?.delete(handler);
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
_emit(event, data) {
|
|
185
|
+
const handlers = this._handlers.get(event);
|
|
186
|
+
if (!handlers) return;
|
|
187
|
+
for (const handler of handlers) {
|
|
188
|
+
try {
|
|
189
|
+
const result = handler(data);
|
|
190
|
+
if (result instanceof Promise) {
|
|
191
|
+
result.catch(
|
|
192
|
+
(err) => console.error(`Error in '${event}' handler:`, err)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.error(`Error in '${event}' handler:`, err);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Start receiving captions from the stream.
|
|
202
|
+
*
|
|
203
|
+
* @returns Promise that resolves when connected.
|
|
204
|
+
*/
|
|
205
|
+
async connect() {
|
|
206
|
+
this._intentionalClose = false;
|
|
207
|
+
let params = `uniqueId=${this.uniqueId}&apiKey=${this.apiKey}`;
|
|
208
|
+
if (this.translate) params += `&translate=${this.translate}`;
|
|
209
|
+
if (this.diarization) params += "&diarization=true";
|
|
210
|
+
if (this.maxDurationMinutes)
|
|
211
|
+
params += `&max_duration_minutes=${this.maxDurationMinutes}`;
|
|
212
|
+
const uri = `${CAPTIONS_BASE}?${params}`;
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
this._ws = new WebSocket2(uri, {
|
|
215
|
+
headers: { "User-Agent": `tiktok-live-api/${VERSION2}` }
|
|
216
|
+
});
|
|
217
|
+
this._ws.on("open", () => {
|
|
218
|
+
this._connected = true;
|
|
219
|
+
this._emit("connected", { uniqueId: this.uniqueId });
|
|
220
|
+
resolve();
|
|
221
|
+
});
|
|
222
|
+
this._ws.on("message", (raw) => {
|
|
223
|
+
try {
|
|
224
|
+
const event = JSON.parse(raw.toString());
|
|
225
|
+
const msgType = event.type || "unknown";
|
|
226
|
+
this._emit(msgType, event);
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
this._ws.on("close", () => {
|
|
231
|
+
this._connected = false;
|
|
232
|
+
this._emit("disconnected", { uniqueId: this.uniqueId });
|
|
233
|
+
});
|
|
234
|
+
this._ws.on("error", (err) => {
|
|
235
|
+
this._emit("error", { error: err.message });
|
|
236
|
+
if (!this._connected) reject(err);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/** Stop receiving captions. */
|
|
241
|
+
disconnect() {
|
|
242
|
+
this._intentionalClose = true;
|
|
243
|
+
if (this._ws) {
|
|
244
|
+
this._ws.close();
|
|
245
|
+
this._ws = null;
|
|
246
|
+
}
|
|
247
|
+
this._connected = false;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
export {
|
|
251
|
+
TikTokCaptions,
|
|
252
|
+
TikTokLive
|
|
253
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tiktok-live-api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Unofficial TikTok LIVE API Client — Real-time chat, gifts, viewers, battles, and AI live captions from any TikTok livestream. Managed WebSocket API with 99.9% uptime.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"tiktok",
|
|
26
|
+
"tiktok-live",
|
|
27
|
+
"tiktok-api",
|
|
28
|
+
"tiktok-live-api",
|
|
29
|
+
"tiktok-live-connector",
|
|
30
|
+
"tiktok-live-sdk",
|
|
31
|
+
"tiktok-websocket",
|
|
32
|
+
"tiktok-chat",
|
|
33
|
+
"tiktok-gifts",
|
|
34
|
+
"tiktok-bot",
|
|
35
|
+
"tiktok-data",
|
|
36
|
+
"tiktok-stream",
|
|
37
|
+
"tiktok-events",
|
|
38
|
+
"tiktok-viewer",
|
|
39
|
+
"live-streaming",
|
|
40
|
+
"live-chat",
|
|
41
|
+
"webcast",
|
|
42
|
+
"websocket",
|
|
43
|
+
"real-time",
|
|
44
|
+
"speech-to-text",
|
|
45
|
+
"captions",
|
|
46
|
+
"transcription",
|
|
47
|
+
"translation",
|
|
48
|
+
"tiktok-live-python",
|
|
49
|
+
"tiktok-tools",
|
|
50
|
+
"euler-stream",
|
|
51
|
+
"tiktoklive",
|
|
52
|
+
"tiktok-connector",
|
|
53
|
+
"tiktok-live-node",
|
|
54
|
+
"tiktok-live-js",
|
|
55
|
+
"tiktok-scraper",
|
|
56
|
+
"tiktok-monitoring",
|
|
57
|
+
"livestream-api"
|
|
58
|
+
],
|
|
59
|
+
"author": {
|
|
60
|
+
"name": "TikTool",
|
|
61
|
+
"email": "support@tik.tools",
|
|
62
|
+
"url": "https://tik.tools"
|
|
63
|
+
},
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "https://github.com/tiktool/tiktok-live-api"
|
|
68
|
+
},
|
|
69
|
+
"bugs": {
|
|
70
|
+
"url": "https://github.com/tiktool/tiktok-live-api/issues"
|
|
71
|
+
},
|
|
72
|
+
"homepage": "https://tik.tools",
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"ws": "^8.16.0"
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@types/ws": "^8.5.10",
|
|
78
|
+
"tsup": "^8.0.0",
|
|
79
|
+
"typescript": "^5.3.0"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=16.0.0"
|
|
83
|
+
}
|
|
84
|
+
}
|