@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.mjs
CHANGED
|
@@ -297,8 +297,10 @@ function parseBattleTeamFromArmies(itemBuf) {
|
|
|
297
297
|
const userBufs = getAllBytes(gf, 1);
|
|
298
298
|
for (const ub of userBufs) {
|
|
299
299
|
try {
|
|
300
|
+
const userFields = decodeProto(ub);
|
|
301
|
+
const individualScore = getInt(userFields, 2);
|
|
300
302
|
const user = parseUser(ub);
|
|
301
|
-
users.push({ user, score:
|
|
303
|
+
users.push({ user, score: individualScore });
|
|
302
304
|
} catch {
|
|
303
305
|
}
|
|
304
306
|
}
|
|
@@ -1129,6 +1131,206 @@ var TikTokLive = class extends EventEmitter {
|
|
|
1129
1131
|
}
|
|
1130
1132
|
};
|
|
1131
1133
|
|
|
1134
|
+
// src/captions.ts
|
|
1135
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
1136
|
+
import WebSocket2 from "ws";
|
|
1137
|
+
var DEFAULT_CAPTIONS_SERVER = "wss://api.tik.tools";
|
|
1138
|
+
var TikTokCaptions = class extends EventEmitter2 {
|
|
1139
|
+
ws = null;
|
|
1140
|
+
_connected = false;
|
|
1141
|
+
intentionalClose = false;
|
|
1142
|
+
reconnectAttempts = 0;
|
|
1143
|
+
uniqueId;
|
|
1144
|
+
apiKey;
|
|
1145
|
+
serverUrl;
|
|
1146
|
+
autoReconnect;
|
|
1147
|
+
maxReconnectAttempts;
|
|
1148
|
+
debug;
|
|
1149
|
+
_translate;
|
|
1150
|
+
_diarization;
|
|
1151
|
+
_maxDurationMinutes;
|
|
1152
|
+
_language;
|
|
1153
|
+
constructor(options) {
|
|
1154
|
+
super();
|
|
1155
|
+
this.uniqueId = options.uniqueId.replace(/^@/, "");
|
|
1156
|
+
if (!options.apiKey) throw new Error("apiKey is required. Get a free key at https://tik.tools");
|
|
1157
|
+
this.apiKey = options.apiKey;
|
|
1158
|
+
this._language = options.language || "";
|
|
1159
|
+
this._translate = options.translate || "";
|
|
1160
|
+
this._diarization = options.diarization ?? true;
|
|
1161
|
+
this._maxDurationMinutes = options.maxDurationMinutes ?? 60;
|
|
1162
|
+
this.serverUrl = (options.signServerUrl || DEFAULT_CAPTIONS_SERVER).replace(/\/$/, "");
|
|
1163
|
+
this.autoReconnect = options.autoReconnect ?? true;
|
|
1164
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
|
|
1165
|
+
this.debug = options.debug ?? false;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Start real-time captions for the configured TikTok user.
|
|
1169
|
+
* Connects to the captions WebSocket relay and begins transcription
|
|
1170
|
+
* once the user goes live (or immediately if already live).
|
|
1171
|
+
*/
|
|
1172
|
+
async start() {
|
|
1173
|
+
this.intentionalClose = false;
|
|
1174
|
+
const wsUrl = this.buildWsUrl();
|
|
1175
|
+
if (this.debug) console.log(`[Captions] Connecting to ${wsUrl}`);
|
|
1176
|
+
return new Promise((resolve, reject) => {
|
|
1177
|
+
this.ws = new WebSocket2(wsUrl);
|
|
1178
|
+
this.ws.on("open", () => {
|
|
1179
|
+
this._connected = true;
|
|
1180
|
+
this.reconnectAttempts = 0;
|
|
1181
|
+
if (this.debug) console.log("[Captions] Connected");
|
|
1182
|
+
this.emit("connected");
|
|
1183
|
+
resolve();
|
|
1184
|
+
});
|
|
1185
|
+
this.ws.on("message", (data) => {
|
|
1186
|
+
this.handleMessage(typeof data === "string" ? data : data.toString());
|
|
1187
|
+
});
|
|
1188
|
+
this.ws.on("close", (code, reason) => {
|
|
1189
|
+
this._connected = false;
|
|
1190
|
+
const reasonStr = reason?.toString() || "";
|
|
1191
|
+
if (this.debug) console.log(`[Captions] Disconnected: ${code} ${reasonStr}`);
|
|
1192
|
+
this.emit("disconnected", code, reasonStr);
|
|
1193
|
+
if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
1194
|
+
this.reconnectAttempts++;
|
|
1195
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), 3e4);
|
|
1196
|
+
if (this.debug) console.log(`[Captions] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
1197
|
+
setTimeout(() => this.start().catch((e) => this.emit("error", { code: "RECONNECT_FAILED", message: e.message })), delay);
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
this.ws.on("error", (err) => {
|
|
1201
|
+
this.emit("error", { code: "WS_ERROR", message: err.message });
|
|
1202
|
+
if (!this._connected) reject(err);
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Stop captions and disconnect.
|
|
1208
|
+
*/
|
|
1209
|
+
stop() {
|
|
1210
|
+
this.intentionalClose = true;
|
|
1211
|
+
if (this.ws) {
|
|
1212
|
+
this.send({ action: "stop" });
|
|
1213
|
+
this.ws.close(1e3);
|
|
1214
|
+
this.ws = null;
|
|
1215
|
+
}
|
|
1216
|
+
this._connected = false;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Switch the translation target language on-the-fly.
|
|
1220
|
+
* Causes a brief interruption while the transcription engine reconfigures.
|
|
1221
|
+
*/
|
|
1222
|
+
setLanguage(language) {
|
|
1223
|
+
this._language = language;
|
|
1224
|
+
this.send({ action: "set_language", language });
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Request a credit balance update from the server.
|
|
1228
|
+
*/
|
|
1229
|
+
getCredits() {
|
|
1230
|
+
this.send({ action: "get_credits" });
|
|
1231
|
+
}
|
|
1232
|
+
/** Whether the WebSocket is currently connected */
|
|
1233
|
+
get connected() {
|
|
1234
|
+
return this._connected;
|
|
1235
|
+
}
|
|
1236
|
+
/** The current target language */
|
|
1237
|
+
get language() {
|
|
1238
|
+
return this._language;
|
|
1239
|
+
}
|
|
1240
|
+
on(event, listener) {
|
|
1241
|
+
return super.on(event, listener);
|
|
1242
|
+
}
|
|
1243
|
+
once(event, listener) {
|
|
1244
|
+
return super.once(event, listener);
|
|
1245
|
+
}
|
|
1246
|
+
off(event, listener) {
|
|
1247
|
+
return super.off(event, listener);
|
|
1248
|
+
}
|
|
1249
|
+
emit(event, ...args) {
|
|
1250
|
+
return super.emit(event, ...args);
|
|
1251
|
+
}
|
|
1252
|
+
buildWsUrl() {
|
|
1253
|
+
const base = this.serverUrl.replace(/^http/, "ws");
|
|
1254
|
+
const params = new URLSearchParams({
|
|
1255
|
+
uniqueId: this.uniqueId,
|
|
1256
|
+
apiKey: this.apiKey
|
|
1257
|
+
});
|
|
1258
|
+
if (this._language) params.set("language", this._language);
|
|
1259
|
+
if (this._translate) params.set("translate", this._translate);
|
|
1260
|
+
if (this._diarization !== void 0) params.set("diarization", String(this._diarization));
|
|
1261
|
+
if (this._maxDurationMinutes) params.set("max_duration_minutes", String(this._maxDurationMinutes));
|
|
1262
|
+
return `${base}/captions?${params}`;
|
|
1263
|
+
}
|
|
1264
|
+
send(msg) {
|
|
1265
|
+
if (this.ws?.readyState === WebSocket2.OPEN) {
|
|
1266
|
+
this.ws.send(JSON.stringify(msg));
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
handleMessage(raw) {
|
|
1270
|
+
try {
|
|
1271
|
+
const msg = JSON.parse(raw);
|
|
1272
|
+
switch (msg.type) {
|
|
1273
|
+
case "caption":
|
|
1274
|
+
this.emit("caption", {
|
|
1275
|
+
text: msg.text,
|
|
1276
|
+
language: msg.language,
|
|
1277
|
+
isFinal: msg.isFinal,
|
|
1278
|
+
confidence: msg.confidence,
|
|
1279
|
+
speaker: msg.speaker,
|
|
1280
|
+
startMs: msg.startMs,
|
|
1281
|
+
endMs: msg.endMs
|
|
1282
|
+
});
|
|
1283
|
+
break;
|
|
1284
|
+
case "translation":
|
|
1285
|
+
this.emit("translation", {
|
|
1286
|
+
text: msg.text,
|
|
1287
|
+
language: msg.language,
|
|
1288
|
+
isFinal: msg.isFinal,
|
|
1289
|
+
confidence: msg.confidence,
|
|
1290
|
+
speaker: msg.speaker
|
|
1291
|
+
});
|
|
1292
|
+
break;
|
|
1293
|
+
case "status":
|
|
1294
|
+
this.emit("status", {
|
|
1295
|
+
status: msg.status,
|
|
1296
|
+
uniqueId: msg.uniqueId,
|
|
1297
|
+
roomId: msg.roomId,
|
|
1298
|
+
language: msg.language,
|
|
1299
|
+
message: msg.message
|
|
1300
|
+
});
|
|
1301
|
+
break;
|
|
1302
|
+
case "credits":
|
|
1303
|
+
this.emit("credits", {
|
|
1304
|
+
remaining: msg.remaining,
|
|
1305
|
+
total: msg.total,
|
|
1306
|
+
used: msg.used,
|
|
1307
|
+
warning: msg.warning
|
|
1308
|
+
});
|
|
1309
|
+
break;
|
|
1310
|
+
case "credits_low":
|
|
1311
|
+
this.emit("credits_low", {
|
|
1312
|
+
remaining: msg.remaining,
|
|
1313
|
+
total: msg.total,
|
|
1314
|
+
percent: msg.percent
|
|
1315
|
+
});
|
|
1316
|
+
break;
|
|
1317
|
+
case "error":
|
|
1318
|
+
this.emit("error", {
|
|
1319
|
+
code: msg.code,
|
|
1320
|
+
message: msg.message
|
|
1321
|
+
});
|
|
1322
|
+
break;
|
|
1323
|
+
default:
|
|
1324
|
+
if (this.debug) {
|
|
1325
|
+
console.log(`[Captions] Unknown message type: ${msg.type}`, msg);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
} catch {
|
|
1329
|
+
if (this.debug) console.error("[Captions] Failed to parse message:", raw);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1132
1334
|
// src/api.ts
|
|
1133
1335
|
var DEFAULT_SIGN_SERVER2 = "https://api.tik.tools";
|
|
1134
1336
|
var PAGE_CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -1185,6 +1387,7 @@ async function getRegionalRanklist(opts) {
|
|
|
1185
1387
|
return data;
|
|
1186
1388
|
}
|
|
1187
1389
|
export {
|
|
1390
|
+
TikTokCaptions,
|
|
1188
1391
|
TikTokLive,
|
|
1189
1392
|
getRanklist,
|
|
1190
1393
|
getRegionalRanklist
|