@jambonz/speech-utils 1.0.3 → 1.0.6
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/.github/workflows/publish.yml +2 -2
- package/index.js +0 -2
- package/lib/get-tts-voices.js +2 -92
- package/lib/synth-audio.js +48 -262
- package/lib/utils.js +1 -101
- package/package.json +1 -3
- package/test/index.js +0 -1
- package/test/list-voices.js +0 -93
- package/test/synth.js +0 -158
- package/build_stubs.sh +0 -11
- package/lib/get-nuance-access-token.js +0 -49
- package/lib/get-verbio-token.js +0 -51
- package/protos/nuance/synthesizer.proto +0 -332
- package/stubs/nuance/synthesizer_grpc_pb.js +0 -104
- package/stubs/nuance/synthesizer_pb.js +0 -6815
- package/test/nuance.js +0 -81
package/lib/utils.js
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
const crypto = require('crypto');
|
|
2
|
-
const {SynthesizerClient} = require('../stubs/nuance/synthesizer_grpc_pb');
|
|
3
2
|
const {RivaSpeechSynthesisClient} = require('../stubs/riva/proto/riva_tts_grpc_pb');
|
|
4
|
-
const {Pool} = require('undici');
|
|
5
|
-
const pool = new Pool('https://auth.crt.nuance.com');
|
|
6
|
-
const NUANCE_AUTH_ENDPOINT = 'tts.api.nuance.com:443';
|
|
7
3
|
const grpc = require('@grpc/grpc-js');
|
|
8
|
-
const
|
|
9
|
-
const { TMP_FOLDER, HTTP_TIMEOUT } = require('./config');
|
|
10
|
-
|
|
11
|
-
const debug = require('debug')('jambonz:realtimedb-helpers');
|
|
12
|
-
/**
|
|
13
|
-
* Future TODO: cache recently used connections to providers
|
|
14
|
-
* to avoid connection overhead during a call.
|
|
15
|
-
* Will need to periodically age them out to avoid memory leaks.
|
|
16
|
-
*/
|
|
17
|
-
//const nuanceClientMap = new Map();
|
|
4
|
+
const { TMP_FOLDER } = require('./config');
|
|
18
5
|
|
|
19
6
|
function makeSynthKey({
|
|
20
7
|
account_sid = '',
|
|
@@ -45,90 +32,12 @@ const noopLogger = {
|
|
|
45
32
|
error: () => {}
|
|
46
33
|
};
|
|
47
34
|
|
|
48
|
-
const toBase64 = (str) => Buffer.from(str || '', 'utf8').toString('base64');
|
|
49
|
-
|
|
50
|
-
function makeBasicAuthHeader(username, password) {
|
|
51
|
-
if (!username || !password) return {};
|
|
52
|
-
const creds = `${encodeURIComponent(username)}:${password || ''}`;
|
|
53
|
-
const header = `Basic ${toBase64(creds)}`;
|
|
54
|
-
return {Authorization: header};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
35
|
function makeAwsKey(awsAccessKeyId) {
|
|
58
36
|
const hash = crypto.createHash('sha1');
|
|
59
37
|
hash.update(awsAccessKeyId);
|
|
60
38
|
return `aws:${hash.digest('hex')}`;
|
|
61
39
|
}
|
|
62
40
|
|
|
63
|
-
function makePlayhtKey(apiKey) {
|
|
64
|
-
const hash = crypto.createHash('sha1');
|
|
65
|
-
hash.update(apiKey);
|
|
66
|
-
return `playht:${hash.digest('hex')}`;
|
|
67
|
-
}
|
|
68
|
-
function makeVerbioKey(client_id) {
|
|
69
|
-
const hash = crypto.createHash('sha1');
|
|
70
|
-
hash.update(client_id);
|
|
71
|
-
return `verbio:${hash.digest('hex')}`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function makeNuanceKey(clientId, secret, scope) {
|
|
75
|
-
const hash = crypto.createHash('sha1');
|
|
76
|
-
hash.update(`${clientId}:${secret}:${scope}`);
|
|
77
|
-
return `nuance:${hash.digest('hex')}`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const getNuanceAccessToken = async(clientId, secret, scope = 'asr tts') => {
|
|
81
|
-
const payload = {
|
|
82
|
-
grant_type: 'client_credentials',
|
|
83
|
-
scope
|
|
84
|
-
};
|
|
85
|
-
const auth = makeBasicAuthHeader(clientId, secret);
|
|
86
|
-
const {statusCode, headers, body} = await pool.request({
|
|
87
|
-
path: '/oauth2/token',
|
|
88
|
-
method: 'POST',
|
|
89
|
-
headers: {
|
|
90
|
-
...auth,
|
|
91
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
92
|
-
},
|
|
93
|
-
body: formurlencoded(payload),
|
|
94
|
-
timeout: HTTP_TIMEOUT,
|
|
95
|
-
followRedirects: false
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (200 !== statusCode) {
|
|
99
|
-
debug({statusCode, headers, body: body.text()}, 'error fetching access token from Nuance');
|
|
100
|
-
const err = new Error();
|
|
101
|
-
err.statusCode = statusCode;
|
|
102
|
-
throw err;
|
|
103
|
-
}
|
|
104
|
-
const json = await body.json();
|
|
105
|
-
return json.access_token;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const createKryptonClient = async(uri) => {
|
|
109
|
-
const client = new SynthesizerClient(uri, grpc.credentials.createInsecure());
|
|
110
|
-
return client;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const createNuanceClient = async(access_token) => {
|
|
114
|
-
|
|
115
|
-
//if (nuanceClientMap.has(access_token)) return nuanceClientMap.get(access_token);
|
|
116
|
-
|
|
117
|
-
const generateMetadata = (params, callback) => {
|
|
118
|
-
var metadata = new grpc.Metadata();
|
|
119
|
-
metadata.add('authorization', `Bearer ${access_token}`);
|
|
120
|
-
callback(null, metadata);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const sslCreds = grpc.credentials.createSsl();
|
|
124
|
-
const authCreds = grpc.credentials.createFromMetadataGenerator(generateMetadata);
|
|
125
|
-
const combined_creds = grpc.credentials.combineChannelCredentials(sslCreds, authCreds);
|
|
126
|
-
const client = new SynthesizerClient(NUANCE_AUTH_ENDPOINT, combined_creds);
|
|
127
|
-
|
|
128
|
-
//if (process.env.NUANCE_CACHE_TTS_CONNECTIONS) nuanceClientMap.set(access_token, client);
|
|
129
|
-
return client;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
41
|
const createRivaClient = async(rivaUri) => {
|
|
133
42
|
const client = new RivaSpeechSynthesisClient(rivaUri, grpc.credentials.createInsecure());
|
|
134
43
|
return client;
|
|
@@ -136,17 +45,8 @@ const createRivaClient = async(rivaUri) => {
|
|
|
136
45
|
|
|
137
46
|
module.exports = {
|
|
138
47
|
makeSynthKey,
|
|
139
|
-
makeNuanceKey,
|
|
140
|
-
|
|
141
|
-
makePlayhtKey,
|
|
142
48
|
makeAwsKey,
|
|
143
|
-
makeVerbioKey,
|
|
144
|
-
getNuanceAccessToken,
|
|
145
|
-
createNuanceClient,
|
|
146
|
-
createKryptonClient,
|
|
147
49
|
createRivaClient,
|
|
148
|
-
makeBasicAuthHeader,
|
|
149
|
-
NUANCE_AUTH_ENDPOINT,
|
|
150
50
|
noopLogger,
|
|
151
51
|
makeFilePath
|
|
152
52
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jambonz/speech-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "TTS-related speech utilities for jambonz",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Dave Horton",
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
"coverage": "nyc --reporter html --report-dir ./coverage npm run test",
|
|
14
14
|
"jslint": "eslint index.js lib",
|
|
15
15
|
"jslint:fix": "npm run jslint --fix",
|
|
16
|
-
"build": "./build_stubs.sh",
|
|
17
16
|
"prepare": "husky"
|
|
18
17
|
},
|
|
19
18
|
"repository": {
|
|
@@ -35,7 +34,6 @@
|
|
|
35
34
|
"@jambonz/realtimedb-helpers": "^0.8.7",
|
|
36
35
|
"bent": "^7.3.12",
|
|
37
36
|
"debug": "^4.3.4",
|
|
38
|
-
"form-urlencoded": "^6.1.4",
|
|
39
37
|
"google-protobuf": "^3.21.2",
|
|
40
38
|
"microsoft-cognitiveservices-speech-sdk": "1.38.0",
|
|
41
39
|
"openai": "^4.98.0",
|
package/test/index.js
CHANGED
package/test/list-voices.js
CHANGED
|
@@ -12,99 +12,6 @@ const stats = {
|
|
|
12
12
|
histogram: () => {}
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
test('Verbio - get Access key and voices', async(t) => {
|
|
16
|
-
const fn = require('..');
|
|
17
|
-
const {client, getTtsVoices, getVerbioAccessToken} = fn(opts, logger);
|
|
18
|
-
if (!process.env.VERBIO_CLIENT_ID || !process.env.VERBIO_CLIENT_SECRET) {
|
|
19
|
-
t.pass('skipping Verbio test since no Verbio Keys provided');
|
|
20
|
-
t.end();
|
|
21
|
-
client.quit();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const credentials = {
|
|
27
|
-
client_id: process.env.VERBIO_CLIENT_ID,
|
|
28
|
-
client_secret: process.env.VERBIO_CLIENT_SECRET
|
|
29
|
-
};
|
|
30
|
-
let obj = await getVerbioAccessToken(credentials);
|
|
31
|
-
t.ok(obj.access_token , 'successfully received access token not from cache');
|
|
32
|
-
const voices = await getTtsVoices({vendor: 'verbio', credentials});
|
|
33
|
-
t.ok(voices && voices.length != 0, 'successfully received verbio voices');
|
|
34
|
-
} catch (err) {
|
|
35
|
-
console.error(err);
|
|
36
|
-
t.end(err);
|
|
37
|
-
}
|
|
38
|
-
client.quit();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('Nuance hosted tests', async(t) => {
|
|
42
|
-
const fn = require('..');
|
|
43
|
-
const {client, getTtsVoices} = fn(opts, logger);
|
|
44
|
-
|
|
45
|
-
if (!process.env.NUANCE_CLIENT_ID || !process.env.NUANCE_SECRET ) {
|
|
46
|
-
t.pass('skipping Nuance hosted test since no Nuance client_id and secret provided');
|
|
47
|
-
t.end();
|
|
48
|
-
client.quit();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
try {
|
|
52
|
-
const opts = {
|
|
53
|
-
vendor: 'nuance',
|
|
54
|
-
credentials: {
|
|
55
|
-
client_id: process.env.NUANCE_CLIENT_ID,
|
|
56
|
-
secret: process.env.NUANCE_SECRET
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
let voices = await getTtsVoices(opts);
|
|
60
|
-
t.ok(voices.length > 0 && voices[0].language,
|
|
61
|
-
`GetVoices: successfully retrieved ${voices.length} voices from Nuance`);
|
|
62
|
-
|
|
63
|
-
await client.flushall();
|
|
64
|
-
|
|
65
|
-
t.end();
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
69
|
-
console.error(err);
|
|
70
|
-
t.end(err);
|
|
71
|
-
}
|
|
72
|
-
client.quit();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('Nuance on-prem tests', async(t) => {
|
|
76
|
-
const fn = require('..');
|
|
77
|
-
const {client, getTtsVoices} = fn(opts, logger);
|
|
78
|
-
|
|
79
|
-
if (!process.env.NUANCE_TTS_URI ) {
|
|
80
|
-
t.pass('skipping Nuance on-prem test since no Nuance uri provided');
|
|
81
|
-
t.end();
|
|
82
|
-
client.quit();
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
try {
|
|
86
|
-
const opts = {
|
|
87
|
-
vendor: 'nuance',
|
|
88
|
-
credentials: {
|
|
89
|
-
nuance_tts_uri: process.env.NUANCE_TTS_URI
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
let voices = await getTtsVoices(opts);
|
|
93
|
-
t.ok(voices.length > 0 && voices[0].language,
|
|
94
|
-
`GetVoices: successfully retrieved ${voices.length} voices from Nuance`);
|
|
95
|
-
|
|
96
|
-
await client.flushall();
|
|
97
|
-
|
|
98
|
-
t.end();
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
console.error(err);
|
|
103
|
-
t.end(err);
|
|
104
|
-
}
|
|
105
|
-
client.quit();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
15
|
test('Google tests', async(t) => {
|
|
109
16
|
const fn = require('..');
|
|
110
17
|
const {client, getTtsVoices} = fn(opts, logger);
|
package/test/synth.js
CHANGED
|
@@ -759,82 +759,6 @@ test('Azure custom voice speech synth tests', async(t) => {
|
|
|
759
759
|
client.quit();
|
|
760
760
|
});
|
|
761
761
|
|
|
762
|
-
test('Nuance hosted speech synth tests', async(t) => {
|
|
763
|
-
const fn = require('..');
|
|
764
|
-
const {synthAudio, client} = fn(opts, logger);
|
|
765
|
-
|
|
766
|
-
if (!process.env.NUANCE_CLIENT_ID || !process.env.NUANCE_SECRET) {
|
|
767
|
-
t.pass('skipping Nuance speech synth tests since NUANCE_CLIENT_ID or NUANCE_SECRET not provided');
|
|
768
|
-
return t.end();
|
|
769
|
-
}
|
|
770
|
-
try {
|
|
771
|
-
let opts = await synthAudio(stats, {
|
|
772
|
-
vendor: 'nuance',
|
|
773
|
-
credentials: {
|
|
774
|
-
client_id: process.env.NUANCE_CLIENT_ID,
|
|
775
|
-
secret: process.env.NUANCE_SECRET,
|
|
776
|
-
},
|
|
777
|
-
language: 'en-US',
|
|
778
|
-
voice: 'Evan',
|
|
779
|
-
text: 'This is a test. This is only a test',
|
|
780
|
-
});
|
|
781
|
-
t.ok(!opts.servedFromCache, `successfully synthesized nuance audio to ${opts.filePath}`);
|
|
782
|
-
|
|
783
|
-
opts = await synthAudio(stats, {
|
|
784
|
-
vendor: 'nuance',
|
|
785
|
-
credentials: {
|
|
786
|
-
client_id: process.env.NUANCE_CLIENT_ID,
|
|
787
|
-
secret: process.env.NUANCE_SECRET,
|
|
788
|
-
},
|
|
789
|
-
language: 'en-US',
|
|
790
|
-
voice: 'Evan',
|
|
791
|
-
text: 'This is a test. This is only a test',
|
|
792
|
-
});
|
|
793
|
-
t.ok(opts.servedFromCache, `successfully retrieved nuance audio from cache ${opts.filePath}`);
|
|
794
|
-
} catch (err) {
|
|
795
|
-
console.error(err);
|
|
796
|
-
t.end(err);
|
|
797
|
-
}
|
|
798
|
-
client.quit();
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
test('Nuance on-prem speech synth tests', async(t) => {
|
|
802
|
-
const fn = require('..');
|
|
803
|
-
const {synthAudio, client} = fn(opts, logger);
|
|
804
|
-
|
|
805
|
-
if (!process.env.NUANCE_TTS_URI) {
|
|
806
|
-
t.pass('skipping Nuance on prem speech synth tests since NUANCE_TTS_URI not provided');
|
|
807
|
-
return t.end();
|
|
808
|
-
}
|
|
809
|
-
try {
|
|
810
|
-
let opts = await synthAudio(stats, {
|
|
811
|
-
vendor: 'nuance',
|
|
812
|
-
credentials: {
|
|
813
|
-
nuance_tts_uri: process.env.NUANCE_TTS_URI
|
|
814
|
-
},
|
|
815
|
-
language: 'en-US',
|
|
816
|
-
voice: 'Evan',
|
|
817
|
-
text: 'This is a test of on-prem. This is only a test',
|
|
818
|
-
});
|
|
819
|
-
t.ok(!opts.servedFromCache, `successfully synthesized nuance audio to ${opts.filePath}`);
|
|
820
|
-
|
|
821
|
-
opts = await synthAudio(stats, {
|
|
822
|
-
vendor: 'nuance',
|
|
823
|
-
credentials: {
|
|
824
|
-
nuance_tts_uri: process.env.NUANCE_TTS_URI
|
|
825
|
-
},
|
|
826
|
-
language: 'en-US',
|
|
827
|
-
voice: 'Evan',
|
|
828
|
-
text: 'This is a test of on-prem. This is only a test',
|
|
829
|
-
});
|
|
830
|
-
t.ok(opts.servedFromCache, `successfully retrieved nuance audio from cache ${opts.filePath}`);
|
|
831
|
-
} catch (err) {
|
|
832
|
-
console.error(err);
|
|
833
|
-
t.end(err);
|
|
834
|
-
}
|
|
835
|
-
client.quit();
|
|
836
|
-
});
|
|
837
|
-
|
|
838
762
|
test('Nvidia speech synth tests', async(t) => {
|
|
839
763
|
const fn = require('..');
|
|
840
764
|
const {synthAudio, client} = fn(opts, logger);
|
|
@@ -975,55 +899,6 @@ test('Elevenlabs speech synth tests', async(t) => {
|
|
|
975
899
|
client.quit();
|
|
976
900
|
});
|
|
977
901
|
|
|
978
|
-
const testPlayHT = async(t, voice_engine) => {
|
|
979
|
-
const fn = require('..');
|
|
980
|
-
const {synthAudio, client} = fn(opts, logger);
|
|
981
|
-
|
|
982
|
-
if (!process.env.PLAYHT_API_KEY || !process.env.PLAYHT_USER_ID) {
|
|
983
|
-
t.pass('skipping PlayHT speech synth tests since PLAYHT_API_KEY or PLAYHT_USER_ID is/are not provided');
|
|
984
|
-
return t.end();
|
|
985
|
-
}
|
|
986
|
-
const text = 'Hi there and welcome to jambones! ' + Date.now();
|
|
987
|
-
try {
|
|
988
|
-
const opts = await synthAudio(stats, {
|
|
989
|
-
vendor: 'playht',
|
|
990
|
-
credentials: {
|
|
991
|
-
api_key: process.env.PLAYHT_API_KEY,
|
|
992
|
-
user_id: process.env.PLAYHT_USER_ID,
|
|
993
|
-
voice_engine,
|
|
994
|
-
options: JSON.stringify({
|
|
995
|
-
quality: 'medium',
|
|
996
|
-
speed: 1,
|
|
997
|
-
seed: 1,
|
|
998
|
-
temperature: 1,
|
|
999
|
-
emotion: 'female_happy',
|
|
1000
|
-
voice_guidance: 3,
|
|
1001
|
-
style_guidance: 20,
|
|
1002
|
-
text_guidance: 1,
|
|
1003
|
-
})
|
|
1004
|
-
},
|
|
1005
|
-
language: 'english',
|
|
1006
|
-
voice: 's3://voice-cloning-zero-shot/d9ff78ba-d016-47f6-b0ef-dd630f59414e/female-cs/manifest.json',
|
|
1007
|
-
text,
|
|
1008
|
-
renderForCaching: true
|
|
1009
|
-
});
|
|
1010
|
-
t.ok(!opts.servedFromCache, `successfully playht eleven audio to ${opts.filePath}`);
|
|
1011
|
-
|
|
1012
|
-
} catch (err) {
|
|
1013
|
-
console.error(JSON.stringify(err));
|
|
1014
|
-
t.end(err);
|
|
1015
|
-
}
|
|
1016
|
-
client.quit();
|
|
1017
|
-
};
|
|
1018
|
-
|
|
1019
|
-
test('PlayHT speech synth tests', async(t) => {
|
|
1020
|
-
await testPlayHT(t, 'PlayHT2.0-turbo');
|
|
1021
|
-
});
|
|
1022
|
-
|
|
1023
|
-
test('PlayHT3.0 speech synth tests', async(t) => {
|
|
1024
|
-
await testPlayHT(t, 'Play3.0');
|
|
1025
|
-
});
|
|
1026
|
-
|
|
1027
902
|
test('Cartesia speech synth tests', async(t) => {
|
|
1028
903
|
const fn = require('..');
|
|
1029
904
|
const {synthAudio, client} = fn(opts, logger);
|
|
@@ -1215,39 +1090,6 @@ test('whisper speech synth tests', async(t) => {
|
|
|
1215
1090
|
client.quit();
|
|
1216
1091
|
});
|
|
1217
1092
|
|
|
1218
|
-
test('Verbio speech synth tests', async(t) => {
|
|
1219
|
-
const fn = require('..');
|
|
1220
|
-
const {synthAudio, client} = fn(opts, logger);
|
|
1221
|
-
|
|
1222
|
-
if (!process.env.VERBIO_CLIENT_ID || !process.env.VERBIO_CLIENT_SECRET) {
|
|
1223
|
-
t.pass('skipping Verbio Synthesize test since no Verbio Keys provided');
|
|
1224
|
-
t.end();
|
|
1225
|
-
client.quit();
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
const text = 'Hi there and welcome to jambones!';
|
|
1230
|
-
try {
|
|
1231
|
-
let opts = await synthAudio(stats, {
|
|
1232
|
-
vendor: 'verbio',
|
|
1233
|
-
credentials: {
|
|
1234
|
-
client_id: process.env.VERBIO_CLIENT_ID,
|
|
1235
|
-
client_secret: process.env.VERBIO_CLIENT_SECRET
|
|
1236
|
-
},
|
|
1237
|
-
language: 'en-US',
|
|
1238
|
-
voice: 'tommy_en-us',
|
|
1239
|
-
text,
|
|
1240
|
-
renderForCaching: true
|
|
1241
|
-
});
|
|
1242
|
-
t.ok(!opts.servedFromCache, `successfully synthesized whisper audio to ${opts.filePath}`);
|
|
1243
|
-
|
|
1244
|
-
} catch (err) {
|
|
1245
|
-
console.error(JSON.stringify(err));
|
|
1246
|
-
t.end(err);
|
|
1247
|
-
}
|
|
1248
|
-
client.quit();
|
|
1249
|
-
})
|
|
1250
|
-
|
|
1251
1093
|
test('Deepgram speech synth tests', async(t) => {
|
|
1252
1094
|
const fn = require('..');
|
|
1253
1095
|
const {synthAudio, client} = fn(opts, logger);
|
package/build_stubs.sh
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
const formurlencoded = require('form-urlencoded');
|
|
2
|
-
const {Pool} = require('undici');
|
|
3
|
-
const pool = new Pool('https://auth.crt.nuance.com');
|
|
4
|
-
const {makeNuanceKey, makeBasicAuthHeader, noopLogger} = require('./utils');
|
|
5
|
-
const { HTTP_TIMEOUT } = require('./config');
|
|
6
|
-
const debug = require('debug')('jambonz:realtimedb-helpers');
|
|
7
|
-
|
|
8
|
-
async function getNuanceAccessToken(client, logger, clientId, secret, scope) {
|
|
9
|
-
logger = logger || noopLogger;
|
|
10
|
-
try {
|
|
11
|
-
const key = makeNuanceKey(clientId, secret, scope);
|
|
12
|
-
const access_token = await client.get(key);
|
|
13
|
-
if (access_token) return {access_token, servedFromCache: true};
|
|
14
|
-
|
|
15
|
-
/* access token not found in cache, so fetch it from Nuance */
|
|
16
|
-
const payload = {
|
|
17
|
-
grant_type: 'client_credentials',
|
|
18
|
-
scope
|
|
19
|
-
};
|
|
20
|
-
const auth = makeBasicAuthHeader(clientId, secret);
|
|
21
|
-
const {statusCode, headers, body} = await pool.request({
|
|
22
|
-
path: '/oauth2/token',
|
|
23
|
-
method: 'POST',
|
|
24
|
-
headers: {
|
|
25
|
-
...auth,
|
|
26
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
27
|
-
},
|
|
28
|
-
body: formurlencoded(payload),
|
|
29
|
-
timeout: HTTP_TIMEOUT,
|
|
30
|
-
followRedirects: false
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
if (200 !== statusCode) {
|
|
34
|
-
logger.debug({statusCode, headers, body: body.text()}, 'error fetching access token from Nuance');
|
|
35
|
-
const err = new Error();
|
|
36
|
-
err.statusCode = statusCode;
|
|
37
|
-
throw err;
|
|
38
|
-
}
|
|
39
|
-
const json = await body.json();
|
|
40
|
-
await client.set(key, json.access_token, 'EX', json.expires_in - 30);
|
|
41
|
-
return {...json, servedFromCache: false};
|
|
42
|
-
} catch (err) {
|
|
43
|
-
debug(err, `getNuanceAccessToken: Error retrieving Nuance access token for client_id ${clientId}`);
|
|
44
|
-
logger.error(err, `getNuanceAccessToken: Error retrieving Nuance access token for client_id ${clientId}`);
|
|
45
|
-
throw err;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = getNuanceAccessToken;
|
package/lib/get-verbio-token.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const {Pool} = require('undici');
|
|
2
|
-
const { noopLogger, makeVerbioKey } = require('./utils');
|
|
3
|
-
const { HTTP_TIMEOUT } = require('./config');
|
|
4
|
-
const pool = new Pool('https://auth.speechcenter.verbio.com:444');
|
|
5
|
-
const debug = require('debug')('jambonz:realtimedb-helpers');
|
|
6
|
-
|
|
7
|
-
async function getVerbioAccessToken(client, logger, credentials) {
|
|
8
|
-
logger = logger || noopLogger;
|
|
9
|
-
const { client_id, client_secret } = credentials;
|
|
10
|
-
try {
|
|
11
|
-
const key = makeVerbioKey(client_id);
|
|
12
|
-
const access_token = await client.get(key);
|
|
13
|
-
if (access_token) {
|
|
14
|
-
return {access_token, servedFromCache: true};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const payload = {
|
|
18
|
-
client_id,
|
|
19
|
-
client_secret
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const {statusCode, headers, body} = await pool.request({
|
|
23
|
-
path: '/api/v1/token',
|
|
24
|
-
method: 'POST',
|
|
25
|
-
headers: {
|
|
26
|
-
'Content-Type': 'application/json',
|
|
27
|
-
'User-Agent': 'jambonz'
|
|
28
|
-
},
|
|
29
|
-
body: JSON.stringify(payload),
|
|
30
|
-
timeout: HTTP_TIMEOUT,
|
|
31
|
-
followRedirects: false
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (200 !== statusCode) {
|
|
35
|
-
logger.debug({statusCode, headers, body: await body.text()}, 'error fetching access token from Verbio');
|
|
36
|
-
const err = new Error();
|
|
37
|
-
err.statusCode = statusCode;
|
|
38
|
-
throw err;
|
|
39
|
-
}
|
|
40
|
-
const json = await body.json();
|
|
41
|
-
const expiry = Math.floor(json.expiration_time - Date.now() / 1000 - 30);
|
|
42
|
-
await client.set(key, json.access_token, 'EX', expiry);
|
|
43
|
-
return {...json, servedFromCache: false};
|
|
44
|
-
} catch (err) {
|
|
45
|
-
debug(err, `getVerbioAccessToken: Error retrieving Verbio access token for client_id ${client_id}`);
|
|
46
|
-
logger.error(err, `getVerbioAccessToken: Error retrieving Verbio access token for client_id ${client_id}`);
|
|
47
|
-
throw err;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = getVerbioAccessToken;
|