assemblyai 3.0.0 → 3.1.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 +15 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +119 -102
- package/dist/index.js +120 -103
- package/dist/services/base.d.ts +6 -4
- package/dist/services/files/index.d.ts +2 -2
- package/dist/services/index.d.ts +1 -1
- package/dist/services/lemur/index.d.ts +2 -2
- package/dist/services/realtime/factory.d.ts +5 -6
- package/dist/services/realtime/index.d.ts +2 -2
- package/dist/services/realtime/service.d.ts +2 -3
- package/dist/services/transcripts/index.d.ts +7 -7
- package/dist/types/asyncapi.generated.d.ts +20 -17
- package/dist/types/files/index.d.ts +1 -2
- package/dist/types/index.d.ts +6 -6
- package/dist/types/openapi.generated.d.ts +134 -108
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/transcripts/index.d.ts +16 -2
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/services/base.ts +44 -3
- package/src/services/files/index.ts +10 -12
- package/src/services/index.ts +10 -8
- package/src/services/lemur/index.ts +28 -27
- package/src/services/realtime/factory.ts +17 -12
- package/src/services/realtime/index.ts +2 -2
- package/src/services/realtime/service.ts +5 -6
- package/src/services/transcripts/index.ts +57 -55
- package/src/types/asyncapi.generated.ts +20 -17
- package/src/types/files/index.ts +2 -1
- package/src/types/index.ts +6 -6
- package/src/types/openapi.generated.ts +136 -108
- package/src/types/services/index.ts +1 -1
- package/src/types/transcripts/index.ts +16 -2
- package/dist/utils/axios.d.ts +0 -3
- package/src/utils/axios.ts +0 -19
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://twitter.com/AssemblyAI)
|
|
9
9
|
[](https://www.youtube.com/@AssemblyAI)
|
|
10
10
|
[
|
|
11
|
-
](https://
|
|
11
|
+
](https://assemblyai.com/discord)
|
|
12
12
|
|
|
13
13
|
# AssemblyAI Node.js SDK
|
|
14
14
|
|
|
@@ -79,7 +79,7 @@ const transcript = await client.transcripts.create({
|
|
|
79
79
|
poll: true,
|
|
80
80
|
// How frequently the transcript is polled in ms. Defaults to 3000.
|
|
81
81
|
pollingInterval: 1000,
|
|
82
|
-
// How long to wait in ms until the "Polling timeout" error is thrown. Defaults to
|
|
82
|
+
// How long to wait in ms until the "Polling timeout" error is thrown. Defaults to infinite (-1).
|
|
83
83
|
pollingTimeout: 5000,
|
|
84
84
|
})
|
|
85
85
|
```
|
|
@@ -92,9 +92,21 @@ This will return the transcript object in its current state. If the transcript i
|
|
|
92
92
|
const transcript = await client.transcripts.get(transcript.id)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
If you disabled polling during transcript creation, you can still poll until the transcript `status` is `completed` or `error` using `waitUntilReady`:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const transcript = await client.transcripts.waitUntilReady(transcript.id,
|
|
99
|
+
{
|
|
100
|
+
// How frequently the transcript is polled in ms. Defaults to 3000.
|
|
101
|
+
pollingInterval: 1000,
|
|
102
|
+
// How long to wait in ms until the "Polling timeout" error is thrown. Defaults to infinite (-1).
|
|
103
|
+
pollingTimeout: 5000,
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
95
107
|
## List transcripts
|
|
96
108
|
|
|
97
|
-
This will return a page of transcripts
|
|
109
|
+
This will return a page of transcripts you created.
|
|
98
110
|
|
|
99
111
|
```javascript
|
|
100
112
|
const page = await client.transcripts.list()
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from "./types";
|
|
2
2
|
export * from "./services";
|
package/dist/index.esm.js
CHANGED
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
import axios, { isAxiosError } from 'axios';
|
|
2
1
|
import WebSocket from 'ws';
|
|
3
|
-
import
|
|
2
|
+
import Stream from 'stream';
|
|
4
3
|
import fs from 'fs';
|
|
5
4
|
|
|
6
|
-
function createAxiosClient(params) {
|
|
7
|
-
const client = axios.create({
|
|
8
|
-
baseURL: params.baseUrl,
|
|
9
|
-
headers: { Authorization: params.apiKey },
|
|
10
|
-
});
|
|
11
|
-
client.interceptors.response.use(undefined, throwApiError);
|
|
12
|
-
return client;
|
|
13
|
-
}
|
|
14
|
-
function throwApiError(error) {
|
|
15
|
-
var _a, _b;
|
|
16
|
-
if (isAxiosError(error) && ((_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error)) {
|
|
17
|
-
return Promise.reject(new Error(error.response.data.error));
|
|
18
|
-
}
|
|
19
|
-
return Promise.reject(error);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
5
|
/******************************************************************************
|
|
23
6
|
Copyright (c) Microsoft Corporation.
|
|
24
7
|
|
|
@@ -57,36 +40,70 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
57
40
|
class BaseService {
|
|
58
41
|
/**
|
|
59
42
|
* Create a new service.
|
|
60
|
-
* @param params The
|
|
43
|
+
* @param params The parameters to use for the service.
|
|
61
44
|
*/
|
|
62
|
-
constructor(
|
|
63
|
-
this.
|
|
45
|
+
constructor(params) {
|
|
46
|
+
this.params = params;
|
|
47
|
+
}
|
|
48
|
+
fetch(input, init) {
|
|
49
|
+
var _a;
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
init = init !== null && init !== void 0 ? init : {};
|
|
52
|
+
init.headers = (_a = init.headers) !== null && _a !== void 0 ? _a : {};
|
|
53
|
+
init.headers = Object.assign({ Authorization: this.params.apiKey, "Content-Type": "application/json" }, init.headers);
|
|
54
|
+
if (!input.startsWith("http"))
|
|
55
|
+
input = this.params.baseUrl + input;
|
|
56
|
+
const response = yield fetch(input, init);
|
|
57
|
+
if (response.status >= 400) {
|
|
58
|
+
let json;
|
|
59
|
+
const text = yield response.text();
|
|
60
|
+
if (text) {
|
|
61
|
+
try {
|
|
62
|
+
json = JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
catch (_b) {
|
|
65
|
+
/* empty */
|
|
66
|
+
}
|
|
67
|
+
if (json === null || json === void 0 ? void 0 : json.error)
|
|
68
|
+
throw new Error(json.error);
|
|
69
|
+
throw new Error(text);
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
|
|
72
|
+
}
|
|
73
|
+
return response;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
fetchJson(input, init) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
const response = yield this.fetch(input, init);
|
|
79
|
+
return response.json();
|
|
80
|
+
});
|
|
64
81
|
}
|
|
65
82
|
}
|
|
66
83
|
|
|
67
84
|
class LemurService extends BaseService {
|
|
68
85
|
summary(params) {
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
return this.fetchJson("/lemur/v3/generate/summary", {
|
|
87
|
+
method: "POST",
|
|
88
|
+
body: JSON.stringify(params),
|
|
72
89
|
});
|
|
73
90
|
}
|
|
74
91
|
questionAnswer(params) {
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
return this.fetchJson("/lemur/v3/generate/question-answer", {
|
|
93
|
+
method: "POST",
|
|
94
|
+
body: JSON.stringify(params),
|
|
78
95
|
});
|
|
79
96
|
}
|
|
80
97
|
actionItems(params) {
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
return this.fetchJson("/lemur/v3/generate/action-items", {
|
|
99
|
+
method: "POST",
|
|
100
|
+
body: JSON.stringify(params),
|
|
84
101
|
});
|
|
85
102
|
}
|
|
86
103
|
task(params) {
|
|
87
|
-
return
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
return this.fetchJson("/lemur/v3/generate/task", {
|
|
105
|
+
method: "POST",
|
|
106
|
+
body: JSON.stringify(params),
|
|
90
107
|
});
|
|
91
108
|
}
|
|
92
109
|
/**
|
|
@@ -94,9 +111,8 @@ class LemurService extends BaseService {
|
|
|
94
111
|
* @param id ID of the LeMUR request
|
|
95
112
|
*/
|
|
96
113
|
purgeRequestData(id) {
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
return data;
|
|
114
|
+
return this.fetchJson(`/lemur/v3/${id}`, {
|
|
115
|
+
method: "DELETE",
|
|
100
116
|
});
|
|
101
117
|
}
|
|
102
118
|
}
|
|
@@ -145,12 +161,11 @@ class RealtimeError extends Error {
|
|
|
145
161
|
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
146
162
|
class RealtimeService {
|
|
147
163
|
constructor(params) {
|
|
148
|
-
var _a, _b
|
|
164
|
+
var _a, _b;
|
|
149
165
|
this.listeners = {};
|
|
150
166
|
this.realtimeUrl = (_a = params.realtimeUrl) !== null && _a !== void 0 ? _a : defaultRealtimeUrl;
|
|
151
167
|
this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
|
|
152
168
|
this.wordBoost = params.wordBoost;
|
|
153
|
-
this.realtimeUrl = (_c = params.realtimeUrl) !== null && _c !== void 0 ? _c : defaultRealtimeUrl;
|
|
154
169
|
if ("apiKey" in params)
|
|
155
170
|
this.apiKey = params.apiKey;
|
|
156
171
|
if ("token" in params)
|
|
@@ -258,7 +273,7 @@ class RealtimeService {
|
|
|
258
273
|
this.socket.send(JSON.stringify(payload));
|
|
259
274
|
}
|
|
260
275
|
stream() {
|
|
261
|
-
const stream = new Writable({
|
|
276
|
+
const stream = new Stream.Writable({
|
|
262
277
|
write: (chunk, encoding, next) => {
|
|
263
278
|
this.sendAudio(chunk);
|
|
264
279
|
next();
|
|
@@ -291,30 +306,33 @@ class RealtimeService {
|
|
|
291
306
|
}
|
|
292
307
|
}
|
|
293
308
|
|
|
294
|
-
class RealtimeServiceFactory {
|
|
295
|
-
constructor(
|
|
296
|
-
|
|
297
|
-
this.
|
|
309
|
+
class RealtimeServiceFactory extends BaseService {
|
|
310
|
+
constructor(params) {
|
|
311
|
+
super(params);
|
|
312
|
+
this.rtFactoryParams = params;
|
|
298
313
|
}
|
|
299
314
|
createService(params) {
|
|
300
315
|
if (!params)
|
|
301
|
-
params = { apiKey: this.
|
|
316
|
+
params = { apiKey: this.rtFactoryParams.apiKey };
|
|
302
317
|
else if (!("token" in params) && !params.apiKey) {
|
|
303
|
-
params.apiKey = this.
|
|
318
|
+
params.apiKey = this.rtFactoryParams.apiKey;
|
|
304
319
|
}
|
|
305
320
|
return new RealtimeService(params);
|
|
306
321
|
}
|
|
307
322
|
createTemporaryToken(params) {
|
|
308
323
|
return __awaiter(this, void 0, void 0, function* () {
|
|
309
|
-
const
|
|
310
|
-
|
|
324
|
+
const data = yield this.fetchJson("/v2/realtime/token", {
|
|
325
|
+
method: "POST",
|
|
326
|
+
body: JSON.stringify(params),
|
|
327
|
+
});
|
|
328
|
+
return data.token;
|
|
311
329
|
});
|
|
312
330
|
}
|
|
313
331
|
}
|
|
314
332
|
|
|
315
333
|
class TranscriptService extends BaseService {
|
|
316
|
-
constructor(
|
|
317
|
-
super(
|
|
334
|
+
constructor(params, files) {
|
|
335
|
+
super(params);
|
|
318
336
|
this.files = files;
|
|
319
337
|
}
|
|
320
338
|
/**
|
|
@@ -331,16 +349,21 @@ class TranscriptService extends BaseService {
|
|
|
331
349
|
const uploadUrl = yield this.files.upload(path);
|
|
332
350
|
params.audio_url = uploadUrl;
|
|
333
351
|
}
|
|
334
|
-
const
|
|
352
|
+
const data = yield this.fetchJson("/v2/transcript", {
|
|
353
|
+
method: "POST",
|
|
354
|
+
body: JSON.stringify(params),
|
|
355
|
+
});
|
|
335
356
|
if ((_a = options === null || options === void 0 ? void 0 : options.poll) !== null && _a !== void 0 ? _a : true) {
|
|
336
|
-
return yield this.
|
|
357
|
+
return yield this.waitUntilReady(data.id, options);
|
|
337
358
|
}
|
|
338
|
-
return
|
|
359
|
+
return data;
|
|
339
360
|
});
|
|
340
361
|
}
|
|
341
|
-
|
|
342
|
-
var _a;
|
|
362
|
+
waitUntilReady(transcriptId, options) {
|
|
363
|
+
var _a, _b;
|
|
343
364
|
return __awaiter(this, void 0, void 0, function* () {
|
|
365
|
+
const pollingInterval = (_a = options === null || options === void 0 ? void 0 : options.pollingInterval) !== null && _a !== void 0 ? _a : 3000;
|
|
366
|
+
const pollingTimeout = (_b = options === null || options === void 0 ? void 0 : options.pollingTimeout) !== null && _b !== void 0 ? _b : -1;
|
|
344
367
|
const startTime = Date.now();
|
|
345
368
|
// eslint-disable-next-line no-constant-condition
|
|
346
369
|
while (true) {
|
|
@@ -348,12 +371,12 @@ class TranscriptService extends BaseService {
|
|
|
348
371
|
if (transcript.status === "completed" || transcript.status === "error") {
|
|
349
372
|
return transcript;
|
|
350
373
|
}
|
|
351
|
-
else if (
|
|
352
|
-
(
|
|
353
|
-
|
|
374
|
+
else if (pollingTimeout > 0 &&
|
|
375
|
+
Date.now() - startTime > pollingTimeout) {
|
|
376
|
+
throw new Error("Polling timeout");
|
|
354
377
|
}
|
|
355
378
|
else {
|
|
356
|
-
|
|
379
|
+
yield new Promise((resolve) => setTimeout(resolve, pollingInterval));
|
|
357
380
|
}
|
|
358
381
|
}
|
|
359
382
|
});
|
|
@@ -364,10 +387,7 @@ class TranscriptService extends BaseService {
|
|
|
364
387
|
* @returns A promise that resolves to the transcript.
|
|
365
388
|
*/
|
|
366
389
|
get(id) {
|
|
367
|
-
return
|
|
368
|
-
const res = yield this.client.get(`/v2/transcript/${id}`);
|
|
369
|
-
return res.data;
|
|
370
|
-
});
|
|
390
|
+
return this.fetchJson(`/v2/transcript/${id}`);
|
|
371
391
|
}
|
|
372
392
|
/**
|
|
373
393
|
* Retrieves a page of transcript listings.
|
|
@@ -376,16 +396,19 @@ class TranscriptService extends BaseService {
|
|
|
376
396
|
list(parameters) {
|
|
377
397
|
return __awaiter(this, void 0, void 0, function* () {
|
|
378
398
|
let url = "/v2/transcript";
|
|
379
|
-
let query;
|
|
380
399
|
if (typeof parameters === "string") {
|
|
381
400
|
url = parameters;
|
|
382
401
|
}
|
|
383
402
|
else if (parameters) {
|
|
384
|
-
|
|
403
|
+
url = `${url}?${new URLSearchParams(Object.keys(parameters).map((key) => {
|
|
404
|
+
var _a;
|
|
405
|
+
return [
|
|
406
|
+
key,
|
|
407
|
+
((_a = parameters[key]) === null || _a === void 0 ? void 0 : _a.toString()) || "",
|
|
408
|
+
];
|
|
409
|
+
}))}`;
|
|
385
410
|
}
|
|
386
|
-
const
|
|
387
|
-
params: query,
|
|
388
|
-
});
|
|
411
|
+
const data = yield this.fetchJson(url);
|
|
389
412
|
for (const transcriptListItem of data.transcripts) {
|
|
390
413
|
transcriptListItem.created = new Date(transcriptListItem.created);
|
|
391
414
|
if (transcriptListItem.completed) {
|
|
@@ -401,27 +424,18 @@ class TranscriptService extends BaseService {
|
|
|
401
424
|
* @returns A promise that resolves to the transcript.
|
|
402
425
|
*/
|
|
403
426
|
delete(id) {
|
|
404
|
-
return
|
|
405
|
-
const res = yield this.client.delete(`/v2/transcript/${id}`);
|
|
406
|
-
return res.data;
|
|
407
|
-
});
|
|
427
|
+
return this.fetchJson(`/v2/transcript/${id}`, { method: "DELETE" });
|
|
408
428
|
}
|
|
409
429
|
/**
|
|
410
430
|
* Search through the transcript for a specific set of keywords.
|
|
411
431
|
* You can search for individual words, numbers, or phrases containing up to five words or numbers.
|
|
412
432
|
* @param id The identifier of the transcript.
|
|
413
|
-
* @param
|
|
433
|
+
* @param words Keywords to search for.
|
|
414
434
|
* @return A promise that resolves to the sentences.
|
|
415
435
|
*/
|
|
416
436
|
wordSearch(id, words) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
params: {
|
|
420
|
-
words: JSON.stringify(words),
|
|
421
|
-
},
|
|
422
|
-
});
|
|
423
|
-
return data;
|
|
424
|
-
});
|
|
437
|
+
const params = new URLSearchParams({ words: words.join(",") });
|
|
438
|
+
return this.fetchJson(`/v2/transcript/${id}/word-search?${params.toString()}`);
|
|
425
439
|
}
|
|
426
440
|
/**
|
|
427
441
|
* Retrieve all sentences of a transcript.
|
|
@@ -429,10 +443,7 @@ class TranscriptService extends BaseService {
|
|
|
429
443
|
* @return A promise that resolves to the sentences.
|
|
430
444
|
*/
|
|
431
445
|
sentences(id) {
|
|
432
|
-
return
|
|
433
|
-
const { data } = yield this.client.get(`/v2/transcript/${id}/sentences`);
|
|
434
|
-
return data;
|
|
435
|
-
});
|
|
446
|
+
return this.fetchJson(`/v2/transcript/${id}/sentences`);
|
|
436
447
|
}
|
|
437
448
|
/**
|
|
438
449
|
* Retrieve all paragraphs of a transcript.
|
|
@@ -440,21 +451,25 @@ class TranscriptService extends BaseService {
|
|
|
440
451
|
* @return A promise that resolves to the paragraphs.
|
|
441
452
|
*/
|
|
442
453
|
paragraphs(id) {
|
|
443
|
-
return
|
|
444
|
-
const { data } = yield this.client.get(`/v2/transcript/${id}/paragraphs`);
|
|
445
|
-
return data;
|
|
446
|
-
});
|
|
454
|
+
return this.fetchJson(`/v2/transcript/${id}/paragraphs`);
|
|
447
455
|
}
|
|
448
456
|
/**
|
|
449
457
|
* Retrieve subtitles of a transcript.
|
|
450
458
|
* @param id The identifier of the transcript.
|
|
451
459
|
* @param format The format of the subtitles.
|
|
460
|
+
* @param chars_per_caption The maximum number of characters per caption.
|
|
452
461
|
* @return A promise that resolves to the subtitles text.
|
|
453
462
|
*/
|
|
454
|
-
subtitles(id, format = "srt") {
|
|
463
|
+
subtitles(id, format = "srt", chars_per_caption) {
|
|
455
464
|
return __awaiter(this, void 0, void 0, function* () {
|
|
456
|
-
|
|
457
|
-
|
|
465
|
+
let url = `/v2/transcript/${id}/${format}`;
|
|
466
|
+
if (chars_per_caption) {
|
|
467
|
+
const params = new URLSearchParams();
|
|
468
|
+
params.set("chars_per_caption", chars_per_caption.toString());
|
|
469
|
+
url += `?${params.toString()}`;
|
|
470
|
+
}
|
|
471
|
+
const response = yield this.fetch(url);
|
|
472
|
+
return yield response.text();
|
|
458
473
|
});
|
|
459
474
|
}
|
|
460
475
|
/**
|
|
@@ -463,10 +478,7 @@ class TranscriptService extends BaseService {
|
|
|
463
478
|
* @return A promise that resolves to the subtitles text.
|
|
464
479
|
*/
|
|
465
480
|
redactions(id) {
|
|
466
|
-
return
|
|
467
|
-
const { data } = yield this.client.get(`/v2/transcript/${id}/redacted-audio`);
|
|
468
|
-
return data;
|
|
469
|
-
});
|
|
481
|
+
return this.fetchJson(`/v2/transcript/${id}/redacted-audio`);
|
|
470
482
|
}
|
|
471
483
|
}
|
|
472
484
|
function getPath(path) {
|
|
@@ -496,28 +508,33 @@ class FileService extends BaseService {
|
|
|
496
508
|
fileData = fs.createReadStream(input);
|
|
497
509
|
else
|
|
498
510
|
fileData = input;
|
|
499
|
-
const
|
|
511
|
+
const data = yield this.fetchJson("/v2/upload", {
|
|
512
|
+
method: "POST",
|
|
513
|
+
body: fileData,
|
|
500
514
|
headers: {
|
|
501
515
|
"Content-Type": "application/octet-stream",
|
|
502
516
|
},
|
|
517
|
+
duplex: "half",
|
|
503
518
|
});
|
|
504
519
|
return data.upload_url;
|
|
505
520
|
});
|
|
506
521
|
}
|
|
507
522
|
}
|
|
508
523
|
|
|
524
|
+
const defaultBaseUrl = "https://api.assemblyai.com";
|
|
509
525
|
class AssemblyAI {
|
|
510
526
|
/**
|
|
511
527
|
* Create a new AssemblyAI client.
|
|
512
528
|
* @param params The parameters for the service, including the API key and base URL, if any.
|
|
513
529
|
*/
|
|
514
530
|
constructor(params) {
|
|
515
|
-
params.baseUrl = params.baseUrl ||
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
this.
|
|
519
|
-
this.
|
|
520
|
-
this.
|
|
531
|
+
params.baseUrl = params.baseUrl || defaultBaseUrl;
|
|
532
|
+
if (params.baseUrl && params.baseUrl.endsWith("/"))
|
|
533
|
+
params.baseUrl = params.baseUrl.slice(0, -1);
|
|
534
|
+
this.files = new FileService(params);
|
|
535
|
+
this.transcripts = new TranscriptService(params, this.files);
|
|
536
|
+
this.lemur = new LemurService(params);
|
|
537
|
+
this.realtime = new RealtimeServiceFactory(params);
|
|
521
538
|
}
|
|
522
539
|
}
|
|
523
540
|
|