@tiktool/live 2.6.1 → 2.6.2
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 +124 -19
- package/dist/index.d.mts +178 -1
- package/dist/index.d.ts +178 -1
- package/dist/index.js +205 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +204 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -4
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
TikTokCaptions: () => TikTokCaptions,
|
|
33
34
|
TikTokLive: () => TikTokLive,
|
|
34
35
|
getRanklist: () => getRanklist,
|
|
35
36
|
getRegionalRanklist: () => getRegionalRanklist
|
|
@@ -335,8 +336,10 @@ function parseBattleTeamFromArmies(itemBuf) {
|
|
|
335
336
|
const userBufs = getAllBytes(gf, 1);
|
|
336
337
|
for (const ub of userBufs) {
|
|
337
338
|
try {
|
|
339
|
+
const userFields = decodeProto(ub);
|
|
340
|
+
const individualScore = getInt(userFields, 2);
|
|
338
341
|
const user = parseUser(ub);
|
|
339
|
-
users.push({ user, score:
|
|
342
|
+
users.push({ user, score: individualScore });
|
|
340
343
|
} catch {
|
|
341
344
|
}
|
|
342
345
|
}
|
|
@@ -1167,6 +1170,206 @@ var TikTokLive = class extends import_events.EventEmitter {
|
|
|
1167
1170
|
}
|
|
1168
1171
|
};
|
|
1169
1172
|
|
|
1173
|
+
// src/captions.ts
|
|
1174
|
+
var import_events2 = require("events");
|
|
1175
|
+
var import_ws2 = __toESM(require("ws"));
|
|
1176
|
+
var DEFAULT_CAPTIONS_SERVER = "wss://api.tik.tools";
|
|
1177
|
+
var TikTokCaptions = class extends import_events2.EventEmitter {
|
|
1178
|
+
ws = null;
|
|
1179
|
+
_connected = false;
|
|
1180
|
+
intentionalClose = false;
|
|
1181
|
+
reconnectAttempts = 0;
|
|
1182
|
+
uniqueId;
|
|
1183
|
+
apiKey;
|
|
1184
|
+
serverUrl;
|
|
1185
|
+
autoReconnect;
|
|
1186
|
+
maxReconnectAttempts;
|
|
1187
|
+
debug;
|
|
1188
|
+
_translate;
|
|
1189
|
+
_diarization;
|
|
1190
|
+
_maxDurationMinutes;
|
|
1191
|
+
_language;
|
|
1192
|
+
constructor(options) {
|
|
1193
|
+
super();
|
|
1194
|
+
this.uniqueId = options.uniqueId.replace(/^@/, "");
|
|
1195
|
+
if (!options.apiKey) throw new Error("apiKey is required. Get a free key at https://tik.tools");
|
|
1196
|
+
this.apiKey = options.apiKey;
|
|
1197
|
+
this._language = options.language || "";
|
|
1198
|
+
this._translate = options.translate || "";
|
|
1199
|
+
this._diarization = options.diarization ?? true;
|
|
1200
|
+
this._maxDurationMinutes = options.maxDurationMinutes ?? 60;
|
|
1201
|
+
this.serverUrl = (options.signServerUrl || DEFAULT_CAPTIONS_SERVER).replace(/\/$/, "");
|
|
1202
|
+
this.autoReconnect = options.autoReconnect ?? true;
|
|
1203
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
|
|
1204
|
+
this.debug = options.debug ?? false;
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Start real-time captions for the configured TikTok user.
|
|
1208
|
+
* Connects to the captions WebSocket relay and begins transcription
|
|
1209
|
+
* once the user goes live (or immediately if already live).
|
|
1210
|
+
*/
|
|
1211
|
+
async start() {
|
|
1212
|
+
this.intentionalClose = false;
|
|
1213
|
+
const wsUrl = this.buildWsUrl();
|
|
1214
|
+
if (this.debug) console.log(`[Captions] Connecting to ${wsUrl}`);
|
|
1215
|
+
return new Promise((resolve, reject) => {
|
|
1216
|
+
this.ws = new import_ws2.default(wsUrl);
|
|
1217
|
+
this.ws.on("open", () => {
|
|
1218
|
+
this._connected = true;
|
|
1219
|
+
this.reconnectAttempts = 0;
|
|
1220
|
+
if (this.debug) console.log("[Captions] Connected");
|
|
1221
|
+
this.emit("connected");
|
|
1222
|
+
resolve();
|
|
1223
|
+
});
|
|
1224
|
+
this.ws.on("message", (data) => {
|
|
1225
|
+
this.handleMessage(typeof data === "string" ? data : data.toString());
|
|
1226
|
+
});
|
|
1227
|
+
this.ws.on("close", (code, reason) => {
|
|
1228
|
+
this._connected = false;
|
|
1229
|
+
const reasonStr = reason?.toString() || "";
|
|
1230
|
+
if (this.debug) console.log(`[Captions] Disconnected: ${code} ${reasonStr}`);
|
|
1231
|
+
this.emit("disconnected", code, reasonStr);
|
|
1232
|
+
if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
1233
|
+
this.reconnectAttempts++;
|
|
1234
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), 3e4);
|
|
1235
|
+
if (this.debug) console.log(`[Captions] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
1236
|
+
setTimeout(() => this.start().catch((e) => this.emit("error", { code: "RECONNECT_FAILED", message: e.message })), delay);
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
this.ws.on("error", (err) => {
|
|
1240
|
+
this.emit("error", { code: "WS_ERROR", message: err.message });
|
|
1241
|
+
if (!this._connected) reject(err);
|
|
1242
|
+
});
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Stop captions and disconnect.
|
|
1247
|
+
*/
|
|
1248
|
+
stop() {
|
|
1249
|
+
this.intentionalClose = true;
|
|
1250
|
+
if (this.ws) {
|
|
1251
|
+
this.send({ action: "stop" });
|
|
1252
|
+
this.ws.close(1e3);
|
|
1253
|
+
this.ws = null;
|
|
1254
|
+
}
|
|
1255
|
+
this._connected = false;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Switch the translation target language on-the-fly.
|
|
1259
|
+
* Causes a brief interruption while the transcription engine reconfigures.
|
|
1260
|
+
*/
|
|
1261
|
+
setLanguage(language) {
|
|
1262
|
+
this._language = language;
|
|
1263
|
+
this.send({ action: "set_language", language });
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Request a credit balance update from the server.
|
|
1267
|
+
*/
|
|
1268
|
+
getCredits() {
|
|
1269
|
+
this.send({ action: "get_credits" });
|
|
1270
|
+
}
|
|
1271
|
+
/** Whether the WebSocket is currently connected */
|
|
1272
|
+
get connected() {
|
|
1273
|
+
return this._connected;
|
|
1274
|
+
}
|
|
1275
|
+
/** The current target language */
|
|
1276
|
+
get language() {
|
|
1277
|
+
return this._language;
|
|
1278
|
+
}
|
|
1279
|
+
on(event, listener) {
|
|
1280
|
+
return super.on(event, listener);
|
|
1281
|
+
}
|
|
1282
|
+
once(event, listener) {
|
|
1283
|
+
return super.once(event, listener);
|
|
1284
|
+
}
|
|
1285
|
+
off(event, listener) {
|
|
1286
|
+
return super.off(event, listener);
|
|
1287
|
+
}
|
|
1288
|
+
emit(event, ...args) {
|
|
1289
|
+
return super.emit(event, ...args);
|
|
1290
|
+
}
|
|
1291
|
+
buildWsUrl() {
|
|
1292
|
+
const base = this.serverUrl.replace(/^http/, "ws");
|
|
1293
|
+
const params = new URLSearchParams({
|
|
1294
|
+
uniqueId: this.uniqueId,
|
|
1295
|
+
apiKey: this.apiKey
|
|
1296
|
+
});
|
|
1297
|
+
if (this._language) params.set("language", this._language);
|
|
1298
|
+
if (this._translate) params.set("translate", this._translate);
|
|
1299
|
+
if (this._diarization !== void 0) params.set("diarization", String(this._diarization));
|
|
1300
|
+
if (this._maxDurationMinutes) params.set("max_duration_minutes", String(this._maxDurationMinutes));
|
|
1301
|
+
return `${base}/captions?${params}`;
|
|
1302
|
+
}
|
|
1303
|
+
send(msg) {
|
|
1304
|
+
if (this.ws?.readyState === import_ws2.default.OPEN) {
|
|
1305
|
+
this.ws.send(JSON.stringify(msg));
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
handleMessage(raw) {
|
|
1309
|
+
try {
|
|
1310
|
+
const msg = JSON.parse(raw);
|
|
1311
|
+
switch (msg.type) {
|
|
1312
|
+
case "caption":
|
|
1313
|
+
this.emit("caption", {
|
|
1314
|
+
text: msg.text,
|
|
1315
|
+
language: msg.language,
|
|
1316
|
+
isFinal: msg.isFinal,
|
|
1317
|
+
confidence: msg.confidence,
|
|
1318
|
+
speaker: msg.speaker,
|
|
1319
|
+
startMs: msg.startMs,
|
|
1320
|
+
endMs: msg.endMs
|
|
1321
|
+
});
|
|
1322
|
+
break;
|
|
1323
|
+
case "translation":
|
|
1324
|
+
this.emit("translation", {
|
|
1325
|
+
text: msg.text,
|
|
1326
|
+
language: msg.language,
|
|
1327
|
+
isFinal: msg.isFinal,
|
|
1328
|
+
confidence: msg.confidence,
|
|
1329
|
+
speaker: msg.speaker
|
|
1330
|
+
});
|
|
1331
|
+
break;
|
|
1332
|
+
case "status":
|
|
1333
|
+
this.emit("status", {
|
|
1334
|
+
status: msg.status,
|
|
1335
|
+
uniqueId: msg.uniqueId,
|
|
1336
|
+
roomId: msg.roomId,
|
|
1337
|
+
language: msg.language,
|
|
1338
|
+
message: msg.message
|
|
1339
|
+
});
|
|
1340
|
+
break;
|
|
1341
|
+
case "credits":
|
|
1342
|
+
this.emit("credits", {
|
|
1343
|
+
remaining: msg.remaining,
|
|
1344
|
+
total: msg.total,
|
|
1345
|
+
used: msg.used,
|
|
1346
|
+
warning: msg.warning
|
|
1347
|
+
});
|
|
1348
|
+
break;
|
|
1349
|
+
case "credits_low":
|
|
1350
|
+
this.emit("credits_low", {
|
|
1351
|
+
remaining: msg.remaining,
|
|
1352
|
+
total: msg.total,
|
|
1353
|
+
percent: msg.percent
|
|
1354
|
+
});
|
|
1355
|
+
break;
|
|
1356
|
+
case "error":
|
|
1357
|
+
this.emit("error", {
|
|
1358
|
+
code: msg.code,
|
|
1359
|
+
message: msg.message
|
|
1360
|
+
});
|
|
1361
|
+
break;
|
|
1362
|
+
default:
|
|
1363
|
+
if (this.debug) {
|
|
1364
|
+
console.log(`[Captions] Unknown message type: ${msg.type}`, msg);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
} catch {
|
|
1368
|
+
if (this.debug) console.error("[Captions] Failed to parse message:", raw);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1170
1373
|
// src/api.ts
|
|
1171
1374
|
var DEFAULT_SIGN_SERVER2 = "https://api.tik.tools";
|
|
1172
1375
|
var PAGE_CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -1224,6 +1427,7 @@ async function getRegionalRanklist(opts) {
|
|
|
1224
1427
|
}
|
|
1225
1428
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1226
1429
|
0 && (module.exports = {
|
|
1430
|
+
TikTokCaptions,
|
|
1227
1431
|
TikTokLive,
|
|
1228
1432
|
getRanklist,
|
|
1229
1433
|
getRegionalRanklist
|