@jambonz/speech-utils 0.0.13 → 0.0.15
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/config/test.json +3 -1
- package/index.js +16 -7
- package/lib/get-ibm-access-token.js +1 -1
- package/lib/get-nuance-access-token.js +1 -1
- package/lib/get-tts-size.js +11 -0
- package/lib/purge-tts-cache.js +5 -5
- package/lib/synth-audio.js +4 -4
- package/package.json +3 -4
- package/test/ibm.js +2 -2
- package/test/list-voices.js +6 -6
- package/test/nuance.js +2 -2
- package/test/synth.js +13 -10
package/config/test.json
CHANGED
package/index.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
const {noopLogger} = require('./lib/utils');
|
|
2
|
-
const
|
|
3
|
-
const redis = promisify(require('redis'));
|
|
2
|
+
const Redis = require('ioredis');
|
|
4
3
|
|
|
5
4
|
module.exports = (opts, logger) => {
|
|
6
|
-
const {host = '127.0.0.1', port = 6379, tls = false} = opts;
|
|
7
5
|
logger = logger || noopLogger;
|
|
6
|
+
const connectionOpts = {...opts};
|
|
7
|
+
// Support legacy app
|
|
8
|
+
if (process.env.JAMBONES_REDIS_USERNAME && process.env.JAMBONES_REDIS_PASSWORD) {
|
|
9
|
+
if (Array.isArray(connectionOpts)) {
|
|
10
|
+
for (const o of opts) {
|
|
11
|
+
o.username = process.env.JAMBONES_REDIS_USERNAME;
|
|
12
|
+
o.password = process.env.JAMBONES_REDIS_PASSWORD;
|
|
13
|
+
}
|
|
14
|
+
} else {
|
|
15
|
+
connectionOpts.username = process.env.JAMBONES_REDIS_USERNAME;
|
|
16
|
+
connectionOpts.password = process.env.JAMBONES_REDIS_PASSWORD;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
8
19
|
|
|
9
|
-
const
|
|
10
|
-
`${process.env.JAMBONES_REDIS_USERNAME}:${process.env.JAMBONES_REDIS_PASSWORD}@${host}:${port}` :
|
|
11
|
-
`${host}:${port}`;
|
|
12
|
-
const client = redis.createClient(tls ? `rediss://${url}` : `redis://${url}`);
|
|
20
|
+
const client = new Redis(connectionOpts);
|
|
13
21
|
['ready', 'connect', 'reconnecting', 'error', 'end', 'warning']
|
|
14
22
|
.forEach((event) => {
|
|
15
23
|
client.on(event, (...args) => {
|
|
@@ -23,6 +31,7 @@ module.exports = (opts, logger) => {
|
|
|
23
31
|
|
|
24
32
|
return {
|
|
25
33
|
client,
|
|
34
|
+
getTtsSize: require('./lib/get-tts-size').bind(null, client, logger),
|
|
26
35
|
purgeTtsCache: require('./lib/purge-tts-cache').bind(null, client, logger),
|
|
27
36
|
synthAudio: require('./lib/synth-audio').bind(null, client, logger),
|
|
28
37
|
getNuanceAccessToken: require('./lib/get-nuance-access-token').bind(null, client, logger),
|
|
@@ -9,7 +9,7 @@ async function getIbmAccessToken(client, logger, apiKey) {
|
|
|
9
9
|
logger = logger || noopLogger;
|
|
10
10
|
try {
|
|
11
11
|
const key = makeIbmKey(apiKey);
|
|
12
|
-
const access_token = await client.
|
|
12
|
+
const access_token = await client.get(key);
|
|
13
13
|
if (access_token) return {access_token, servedFromCache: true};
|
|
14
14
|
|
|
15
15
|
/* access token not found in cache, so fetch it from Ibm */
|
|
@@ -9,7 +9,7 @@ async function getNuanceAccessToken(client, logger, clientId, secret, scope) {
|
|
|
9
9
|
logger = logger || noopLogger;
|
|
10
10
|
try {
|
|
11
11
|
const key = makeNuanceKey(clientId, secret, scope);
|
|
12
|
-
const access_token = await client.
|
|
12
|
+
const access_token = await client.get(key);
|
|
13
13
|
if (access_token) return {access_token, servedFromCache: true};
|
|
14
14
|
|
|
15
15
|
/* access token not found in cache, so fetch it from Nuance */
|
package/lib/purge-tts-cache.js
CHANGED
|
@@ -19,12 +19,12 @@ async function purgeTtsCache(client, logger, {all, account_sid, vendor,
|
|
|
19
19
|
|
|
20
20
|
try {
|
|
21
21
|
if (all) {
|
|
22
|
-
const keys = await client.
|
|
23
|
-
purgedCount = await client.
|
|
22
|
+
const keys = await client.keys('tts:*');
|
|
23
|
+
purgedCount = await client.del(keys);
|
|
24
24
|
|
|
25
25
|
} else if (account_sid && !vendor && !language && !voice && !engine && !text) {
|
|
26
|
-
const keys = await client.
|
|
27
|
-
purgedCount = await client.
|
|
26
|
+
const keys = await client.keys(`tts:${account_sid}:*`);
|
|
27
|
+
purgedCount = await client.del(keys);
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
30
|
const key = makeSynthKey({
|
|
@@ -35,7 +35,7 @@ async function purgeTtsCache(client, logger, {all, account_sid, vendor,
|
|
|
35
35
|
engine,
|
|
36
36
|
text,
|
|
37
37
|
});
|
|
38
|
-
purgedCount = await client.
|
|
38
|
+
purgedCount = await client.del(key);
|
|
39
39
|
if (purgedCount === 0) error = 'Specified item not found';
|
|
40
40
|
}
|
|
41
41
|
|
package/lib/synth-audio.js
CHANGED
|
@@ -30,7 +30,7 @@ const {
|
|
|
30
30
|
const {SynthesizeSpeechRequest} = require('../stubs/riva/proto/riva_tts_pb');
|
|
31
31
|
const {AudioEncoding} = require('../stubs/riva/proto/riva_audio_pb');
|
|
32
32
|
const debug = require('debug')('jambonz:realtimedb-helpers');
|
|
33
|
-
const EXPIRES = (process.env.JAMBONES_TTS_CACHE_DURATION_MINS ||
|
|
33
|
+
const EXPIRES = (process.env.JAMBONES_TTS_CACHE_DURATION_MINS || 4 * 60) * 60; // cache tts for 4 hours
|
|
34
34
|
const TMP_FOLDER = '/tmp';
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -111,7 +111,7 @@ async function synthAudio(client, logger, stats, { account_sid,
|
|
|
111
111
|
debug(`synth key is ${key}`);
|
|
112
112
|
let cached;
|
|
113
113
|
if (!disableTtsCache) {
|
|
114
|
-
cached = await client.
|
|
114
|
+
cached = await client.get(key);
|
|
115
115
|
}
|
|
116
116
|
if (cached) {
|
|
117
117
|
// found in cache - extend the expiry and use it
|
|
@@ -119,7 +119,7 @@ async function synthAudio(client, logger, stats, { account_sid,
|
|
|
119
119
|
servedFromCache = true;
|
|
120
120
|
stats.increment('tts.cache.requests', ['found:yes']);
|
|
121
121
|
audioBuffer = Buffer.from(cached, 'base64');
|
|
122
|
-
client.
|
|
122
|
+
client.expire(key, EXPIRES).catch((err) => logger.info(err, 'Error setting expires'));
|
|
123
123
|
}
|
|
124
124
|
if (!cached) {
|
|
125
125
|
// not found in cache - go get it from speech vendor and add to cache
|
|
@@ -168,7 +168,7 @@ async function synthAudio(client, logger, stats, { account_sid,
|
|
|
168
168
|
debug(`tts rtt time for ${text.length} chars on ${vendorLabel}: ${rtt}`);
|
|
169
169
|
logger.info(`tts rtt time for ${text.length} chars on ${vendorLabel}: ${rtt}`);
|
|
170
170
|
|
|
171
|
-
client.
|
|
171
|
+
client.setex(key, EXPIRES, audioBuffer.toString('base64'))
|
|
172
172
|
.catch((err) => logger.error(err, `error calling setex on key ${key}`));
|
|
173
173
|
}
|
|
174
174
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jambonz/speech-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "TTS-related speech utilities for jambonz",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Dave Horton",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"test": "test"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"test": "NODE_ENV=test
|
|
12
|
+
"test": "NODE_ENV=test node test/ ",
|
|
13
13
|
"coverage": "nyc --reporter html --report-dir ./coverage npm run test",
|
|
14
14
|
"jslint": "eslint index.js lib",
|
|
15
15
|
"build": "./build_stubs.sh"
|
|
@@ -27,14 +27,13 @@
|
|
|
27
27
|
"@aws-sdk/client-polly": "^3.303.0",
|
|
28
28
|
"@google-cloud/text-to-speech": "^4.2.1",
|
|
29
29
|
"@grpc/grpc-js": "^1.8.13",
|
|
30
|
-
"@jambonz/promisify-redis": "^0.0.6",
|
|
31
30
|
"bent": "^7.3.12",
|
|
32
31
|
"debug": "^4.3.4",
|
|
33
32
|
"form-urlencoded": "^6.1.0",
|
|
34
33
|
"google-protobuf": "^3.21.2",
|
|
35
34
|
"ibm-watson": "^8.0.0",
|
|
36
35
|
"microsoft-cognitiveservices-speech-sdk": "^1.26.0",
|
|
37
|
-
"
|
|
36
|
+
"ioredis": "^5.3.2",
|
|
38
37
|
"undici": "^5.21.0"
|
|
39
38
|
},
|
|
40
39
|
"devDependencies": {
|
package/test/ibm.js
CHANGED
|
@@ -31,7 +31,7 @@ test('IBM - create access key', async(t) => {
|
|
|
31
31
|
//console.log({obj}, 'received access token from IBM - second request');
|
|
32
32
|
t.ok(obj.access_token && obj.servedFromCache, 'successfully received access token from cache');
|
|
33
33
|
|
|
34
|
-
await client.
|
|
34
|
+
await client.flushall();
|
|
35
35
|
t.end();
|
|
36
36
|
}
|
|
37
37
|
catch (err) {
|
|
@@ -65,7 +65,7 @@ test('IBM - retrieve tts voices test', async(t) => {
|
|
|
65
65
|
t.ok(voices.length > 0 && voices[0].language,
|
|
66
66
|
`GetVoices: successfully retrieved ${voices.length} voices from IBM`);
|
|
67
67
|
|
|
68
|
-
await client.
|
|
68
|
+
await client.flushall();
|
|
69
69
|
|
|
70
70
|
t.end();
|
|
71
71
|
|
package/test/list-voices.js
CHANGED
|
@@ -31,7 +31,7 @@ test('IBM - create access key', async(t) => {
|
|
|
31
31
|
//console.log({obj}, 'received access token from IBM - second request');
|
|
32
32
|
t.ok(obj.access_token && obj.servedFromCache, 'successfully received access token from cache');
|
|
33
33
|
|
|
34
|
-
await client.
|
|
34
|
+
await client.flushall();
|
|
35
35
|
t.end();
|
|
36
36
|
}
|
|
37
37
|
catch (err) {
|
|
@@ -65,7 +65,7 @@ test('IBM - retrieve tts voices test', async(t) => {
|
|
|
65
65
|
t.ok(voices.length > 0 && voices[0].language,
|
|
66
66
|
`GetVoices: successfully retrieved ${voices.length} voices from IBM`);
|
|
67
67
|
|
|
68
|
-
await client.
|
|
68
|
+
await client.flushall();
|
|
69
69
|
|
|
70
70
|
t.end();
|
|
71
71
|
|
|
@@ -99,7 +99,7 @@ test('Nuance hosted tests', async(t) => {
|
|
|
99
99
|
t.ok(voices.length > 0 && voices[0].language,
|
|
100
100
|
`GetVoices: successfully retrieved ${voices.length} voices from Nuance`);
|
|
101
101
|
|
|
102
|
-
await client.
|
|
102
|
+
await client.flushall();
|
|
103
103
|
|
|
104
104
|
t.end();
|
|
105
105
|
|
|
@@ -132,7 +132,7 @@ test('Nuance on-prem tests', async(t) => {
|
|
|
132
132
|
t.ok(voices.length > 0 && voices[0].language,
|
|
133
133
|
`GetVoices: successfully retrieved ${voices.length} voices from Nuance`);
|
|
134
134
|
|
|
135
|
-
await client.
|
|
135
|
+
await client.flushall();
|
|
136
136
|
|
|
137
137
|
t.end();
|
|
138
138
|
|
|
@@ -162,7 +162,7 @@ test('Google tests', async(t) => {
|
|
|
162
162
|
let result = await getTtsVoices(opts);
|
|
163
163
|
t.ok(result[0].voices.length > 0, `GetVoices: successfully retrieved ${result[0].voices.length} voices from Google`);
|
|
164
164
|
|
|
165
|
-
await client.
|
|
165
|
+
await client.flushall();
|
|
166
166
|
|
|
167
167
|
t.end();
|
|
168
168
|
}
|
|
@@ -193,7 +193,7 @@ test('AWS tests', async(t) => {
|
|
|
193
193
|
let result = await getTtsVoices(opts);
|
|
194
194
|
t.ok(result?.Voices?.length > 0, `GetVoices: successfully retrieved ${result.Voices.length} voices from AWS`);
|
|
195
195
|
|
|
196
|
-
await client.
|
|
196
|
+
await client.flushall();
|
|
197
197
|
|
|
198
198
|
t.end();
|
|
199
199
|
}
|
package/test/nuance.js
CHANGED
|
@@ -34,7 +34,7 @@ test('Nuance hosted tests', async(t) => {
|
|
|
34
34
|
t.ok(voices.length > 0 && voices[0].language,
|
|
35
35
|
`GetVoices: successfully retrieved ${voices.length} voices from Nuance`);
|
|
36
36
|
|
|
37
|
-
await client.
|
|
37
|
+
await client.flushall();
|
|
38
38
|
|
|
39
39
|
t.end();
|
|
40
40
|
|
|
@@ -67,7 +67,7 @@ test('Nuance on-prem tests', async(t) => {
|
|
|
67
67
|
t.ok(voices.length > 0 && voices[0].language,
|
|
68
68
|
`GetVoices: successfully retrieved ${voices.length} voices from Nuance`);
|
|
69
69
|
|
|
70
|
-
await client.
|
|
70
|
+
await client.flushall();
|
|
71
71
|
|
|
72
72
|
t.end();
|
|
73
73
|
|
package/test/synth.js
CHANGED
|
@@ -413,18 +413,21 @@ test('Custom Vendor speech synth tests', async(t) => {
|
|
|
413
413
|
|
|
414
414
|
test('TTS Cache tests', async(t) => {
|
|
415
415
|
const fn = require('..');
|
|
416
|
-
const {purgeTtsCache, client} = fn(opts, logger);
|
|
416
|
+
const {purgeTtsCache, getTtsSize, client} = fn(opts, logger);
|
|
417
417
|
|
|
418
418
|
try {
|
|
419
419
|
// save some random tts keys to cache
|
|
420
420
|
const minRecords = 8;
|
|
421
421
|
for (const i in Array(minRecords).fill(0)) {
|
|
422
|
-
await client.
|
|
422
|
+
await client.set(makeSynthKey({vendor: i, language: i, voice: i, engine: i, text: i}), i);
|
|
423
423
|
}
|
|
424
|
+
const count = await getTtsSize();
|
|
425
|
+
t.ok(count >= minRecords, 'getTtsSize worked.');
|
|
426
|
+
|
|
424
427
|
const {purgedCount} = await purgeTtsCache();
|
|
425
428
|
t.ok(purgedCount >= minRecords, `successfully purged at least ${minRecords} tts records from cache`);
|
|
426
429
|
|
|
427
|
-
const cached = (await client.
|
|
430
|
+
const cached = (await client.keys('tts:*')).length;
|
|
428
431
|
t.equal(cached, 0, `successfully purged all tts records from cache`);
|
|
429
432
|
|
|
430
433
|
} catch (err) {
|
|
@@ -435,11 +438,11 @@ test('TTS Cache tests', async(t) => {
|
|
|
435
438
|
try {
|
|
436
439
|
// save some random tts keys to cache
|
|
437
440
|
for (const i in Array(10).fill(0)) {
|
|
438
|
-
await client.
|
|
441
|
+
await client.set(makeSynthKey({vendor: i, language: i, voice: i, engine: i, text: i}), i);
|
|
439
442
|
}
|
|
440
443
|
// save a specific key to tts cache
|
|
441
444
|
const opts = {vendor: 'aws', language: 'en-US', voice: 'MALE', engine: 'Engine', text: 'Hello World!'};
|
|
442
|
-
await client.
|
|
445
|
+
await client.set(makeSynthKey(opts), opts.text);
|
|
443
446
|
|
|
444
447
|
const {purgedCount} = await purgeTtsCache({all: false, ...opts});
|
|
445
448
|
t.ok(purgedCount === 1, `successfully purged one specific tts record from cache`);
|
|
@@ -455,7 +458,7 @@ test('TTS Cache tests', async(t) => {
|
|
|
455
458
|
t.ok(error, `error returned when specified key was not found`);
|
|
456
459
|
|
|
457
460
|
// make sure other tts keys are still there
|
|
458
|
-
const cached = (await client.
|
|
461
|
+
const cached = (await client.keys('tts:*')).length;
|
|
459
462
|
t.ok(cached >= 1, `successfully kept all non-specified tts records in cache`);
|
|
460
463
|
|
|
461
464
|
} catch (err) {
|
|
@@ -471,21 +474,21 @@ test('TTS Cache tests', async(t) => {
|
|
|
471
474
|
const account_sid = "12412512_cabc_5aff"
|
|
472
475
|
const account_sid2 = "22412512_cabc_5aff"
|
|
473
476
|
for (const i in Array(minRecords).fill(0)) {
|
|
474
|
-
await client.
|
|
477
|
+
await client.set(makeSynthKey({account_sid, vendor: i, language: i, voice: i, engine: i, text: i}), i);
|
|
475
478
|
}
|
|
476
479
|
for (const i in Array(minRecords).fill(0)) {
|
|
477
|
-
await client.
|
|
480
|
+
await client.set(makeSynthKey({account_sid: account_sid2, vendor: i, language: i, voice: i, engine: i, text: i}), i);
|
|
478
481
|
}
|
|
479
482
|
const {purgedCount} = await purgeTtsCache({account_sid});
|
|
480
483
|
t.equal(purgedCount, minRecords, `successfully purged at least ${minRecords} tts records from cache for account_sid:${account_sid}`);
|
|
481
484
|
|
|
482
|
-
let cached = (await client.
|
|
485
|
+
let cached = (await client.keys('tts:*')).length;
|
|
483
486
|
t.equal(cached, minRecords, `successfully purged all tts records from cache for account_sid:${account_sid}`);
|
|
484
487
|
|
|
485
488
|
const {purgedCount: purgedCount2} = await purgeTtsCache({account_sid: account_sid2});
|
|
486
489
|
t.equal(purgedCount2, minRecords, `successfully purged at least ${minRecords} tts records from cache for account_sid:${account_sid2}`);
|
|
487
490
|
|
|
488
|
-
cached = (await client.
|
|
491
|
+
cached = (await client.keys('tts:*')).length;
|
|
489
492
|
t.equal(cached, 0, `successfully purged all tts records from cache`);
|
|
490
493
|
|
|
491
494
|
} catch (err) {
|