mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-01-25 02:08:24 +00:00
Compare commits
17 Commits
v0.8.5-14
...
v0.9.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a299bc3ca | ||
|
|
70c9407742 | ||
|
|
dba66d58fc | ||
|
|
0ff3d22faf | ||
|
|
187a428a75 | ||
|
|
a4792a521f | ||
|
|
3ac9693735 | ||
|
|
3ad54a0e72 | ||
|
|
bd8fb2f9db | ||
|
|
32b317ae68 | ||
|
|
40e8d08727 | ||
|
|
256ca440a0 | ||
|
|
68d73345ef | ||
|
|
54dd72ff66 | ||
|
|
832a4e8032 | ||
|
|
33c3b99e2e | ||
|
|
8b2a2e196e |
@@ -551,7 +551,7 @@
|
||||
</location>
|
||||
<size>
|
||||
<width>293.00</width>
|
||||
<height>540.00</height>
|
||||
<height>560.00</height>
|
||||
</size>
|
||||
<zorder>6</zorder>
|
||||
<SQLField>
|
||||
|
||||
@@ -190,7 +190,10 @@ const sql = {
|
||||
'ALTER TABLE google_custom_voices ADD FOREIGN KEY speech_credential_sid_idxfk (speech_credential_sid) REFERENCES speech_credentials (speech_credential_sid) ON DELETE CASCADE',
|
||||
'ALTER TABLE clients ADD COLUMN allow_direct_queue_calling BOOLEAN NOT NULL DEFAULT 1',
|
||||
'ALTER TABLE clients ADD COLUMN allow_direct_user_calling BOOLEAN NOT NULL DEFAULT 1',
|
||||
'ALTER TABLE clients ADD COLUMN allow_direct_app_calling BOOLEAN NOT NULL DEFAULT 1'
|
||||
'ALTER TABLE clients ADD COLUMN allow_direct_app_calling BOOLEAN NOT NULL DEFAULT 1',
|
||||
],
|
||||
9000: [
|
||||
'ALTER TABLE sip_gateways ADD COLUMN send_options_ping BOOLEAN NOT NULL DEFAULT 0',
|
||||
]
|
||||
};
|
||||
|
||||
@@ -223,6 +226,7 @@ const doIt = async() => {
|
||||
if (val < 8003) upgrades.push(...sql['8003']);
|
||||
if (val < 8004) upgrades.push(...sql['8004']);
|
||||
if (val < 8005) upgrades.push(...sql['8005']);
|
||||
if (val < 9000) upgrades.push(...sql['9000']);
|
||||
|
||||
// perform all upgrades
|
||||
logger.info({upgrades}, 'applying schema upgrades..');
|
||||
|
||||
@@ -199,8 +199,9 @@ class Account extends Model {
|
||||
debug(r3, 'Account.activateSubscription - replaced old subscription');
|
||||
|
||||
/* update account.plan to paid, if it isnt already */
|
||||
/* update account.is_active to 1, if account is deactivated */
|
||||
await promisePool.execute(
|
||||
'UPDATE accounts SET plan_type = \'paid\' WHERE account_sid = ?',
|
||||
'UPDATE accounts SET plan_type = \'paid\', is_active = 1 WHERE account_sid = ?',
|
||||
[account_sid]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ VoipCarrier.fields = [
|
||||
name: 'requires_register',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
name: 'register_use_tls',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
name: 'register_username',
|
||||
type: 'string'
|
||||
|
||||
@@ -1,28 +1,58 @@
|
||||
const { Writable } = require('stream');
|
||||
const { BlobServiceClient } = require('@azure/storage-blob');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const streamBuffers = require('stream-buffers');
|
||||
|
||||
class AzureStorageUploadStream extends Writable {
|
||||
constructor(logger, opts) {
|
||||
super(opts);
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(opts.connection_string);
|
||||
this.blockBlobClient = blobServiceClient.getContainerClient(opts.bucketName).getBlockBlobClient(opts.Key);
|
||||
|
||||
this.metadata = opts.metadata;
|
||||
this.blocks = [];
|
||||
|
||||
this.bufferSize = 2 * 1024 * 1024; // Buffer size set to 2MB
|
||||
this.buffer = new streamBuffers.WritableStreamBuffer({
|
||||
initialSize: this.bufferSize,
|
||||
incrementAmount: this.bufferSize
|
||||
});
|
||||
}
|
||||
|
||||
async _write(chunk, encoding, callback) {
|
||||
const blockID = uuidv4().replace(/-/g, '');
|
||||
this.blocks.push(blockID);
|
||||
try {
|
||||
await this.blockBlobClient.stageBlock(blockID, chunk, chunk.length);
|
||||
this.buffer.write(chunk, encoding);
|
||||
|
||||
if (this.buffer.size() >= this.bufferSize) {
|
||||
const blockID = uuidv4().replace(/-/g, '');
|
||||
this.blocks.push(blockID);
|
||||
|
||||
try {
|
||||
const dataToWrite = this.buffer.getContents();
|
||||
await this.blockBlobClient.stageBlock(blockID, dataToWrite, dataToWrite.length);
|
||||
callback();
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
} else {
|
||||
callback();
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
|
||||
async _final(callback) {
|
||||
// Write any remaining data in buffer
|
||||
if (this.buffer.size() > 0) {
|
||||
const remainingData = this.buffer.getContents();
|
||||
const blockID = uuidv4().replace(/-/g, '');
|
||||
this.blocks.push(blockID);
|
||||
|
||||
try {
|
||||
await this.blockBlobClient.stageBlock(blockID, remainingData, remainingData.length);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.blockBlobClient.commitBlockList(this.blocks);
|
||||
// remove all null/undefined props
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const { Storage } = require('@google-cloud/storage');
|
||||
const { Writable } = require('stream');
|
||||
const streamBuffers = require('stream-buffers');
|
||||
|
||||
class GoogleStorageUploadStream extends Writable {
|
||||
|
||||
@@ -12,18 +13,38 @@ class GoogleStorageUploadStream extends Writable {
|
||||
this.gcsFile = storage.bucket(opts.bucketName).file(opts.Key);
|
||||
this.writeStream = this.gcsFile.createWriteStream();
|
||||
|
||||
this.bufferSize = 2 * 1024 * 1024; // Buffer size set to 2MB
|
||||
this.buffer = new streamBuffers.WritableStreamBuffer({
|
||||
initialSize: this.bufferSize,
|
||||
incrementAmount: this.bufferSize
|
||||
});
|
||||
|
||||
this.writeStream.on('error', (err) => this.logger.error(err));
|
||||
this.writeStream.on('finish', () => {
|
||||
this.logger.info('google storage Upload completed.');
|
||||
this.logger.info('Google storage Upload completed.');
|
||||
this._addMetadata();
|
||||
});
|
||||
}
|
||||
|
||||
_write(chunk, encoding, callback) {
|
||||
this.writeStream.write(chunk, encoding, callback);
|
||||
this.buffer.write(chunk, encoding);
|
||||
|
||||
// Write to GCS when buffer reaches desired size
|
||||
if (this.buffer.size() >= this.bufferSize) {
|
||||
const dataToWrite = this.buffer.getContents();
|
||||
this.writeStream.write(dataToWrite, callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
_final(callback) {
|
||||
// Write any remaining data in the buffer to GCS
|
||||
if (this.buffer.size() > 0) {
|
||||
const remainingData = this.buffer.getContents();
|
||||
this.writeStream.write(remainingData);
|
||||
}
|
||||
|
||||
this.writeStream.end();
|
||||
this.writeStream.once('finish', callback);
|
||||
}
|
||||
@@ -33,7 +54,7 @@ class GoogleStorageUploadStream extends Writable {
|
||||
await this.gcsFile.setMetadata({metadata: this.metadata});
|
||||
this.logger.info('Google storage Upload and metadata setting completed.');
|
||||
} catch (err) {
|
||||
this.logger.error(err, 'Google storage An error occurred while setting metadata');
|
||||
this.logger.error(err, 'Google storage An error occurred while setting metadata');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class S3MultipartUploadStream extends Writable {
|
||||
this.partNumber = 1;
|
||||
this.multipartETags = [];
|
||||
this.buffer = Buffer.alloc(0);
|
||||
this.minPartSize = 5 * 1024 * 1024; // 5 MB
|
||||
this.minPartSize = 2 * 1024 * 1024; // 5 MB
|
||||
this.s3 = new S3Client(opts.bucketCredential);
|
||||
this.metadata = opts.metadata;
|
||||
}
|
||||
|
||||
@@ -116,6 +116,8 @@ const encryptCredential = (obj) => {
|
||||
secret,
|
||||
nuance_tts_uri,
|
||||
nuance_stt_uri,
|
||||
deepgram_stt_uri,
|
||||
deepgram_stt_use_tls,
|
||||
use_custom_tts,
|
||||
custom_tts_endpoint,
|
||||
custom_tts_endpoint_url,
|
||||
@@ -185,8 +187,11 @@ const encryptCredential = (obj) => {
|
||||
return encrypt(nuanceData);
|
||||
|
||||
case 'deepgram':
|
||||
assert(api_key, 'invalid deepgram speech credential: api_key is required');
|
||||
const deepgramData = JSON.stringify({api_key});
|
||||
// API key is optional if onprem
|
||||
if (!deepgram_stt_uri) {
|
||||
assert(api_key, 'invalid deepgram speech credential: api_key is required');
|
||||
}
|
||||
const deepgramData = JSON.stringify({api_key, deepgram_stt_uri, deepgram_stt_use_tls});
|
||||
return encrypt(deepgramData);
|
||||
|
||||
case 'ibm':
|
||||
@@ -413,7 +418,9 @@ router.put('/:sid', async(req, res) => {
|
||||
custom_tts_url,
|
||||
cobalt_server_uri,
|
||||
model_id,
|
||||
options
|
||||
options,
|
||||
deepgram_stt_uri,
|
||||
deepgram_stt_use_tls,
|
||||
} = req.body;
|
||||
|
||||
const newCred = {
|
||||
@@ -436,7 +443,9 @@ router.put('/:sid', async(req, res) => {
|
||||
custom_tts_url,
|
||||
cobalt_server_uri,
|
||||
model_id,
|
||||
options
|
||||
options,
|
||||
deepgram_stt_uri,
|
||||
deepgram_stt_use_tls,
|
||||
};
|
||||
logger.info({o, newCred}, 'updating speech credential with this new credential');
|
||||
obj.credential = encryptCredential(newCred);
|
||||
@@ -647,7 +656,7 @@ router.get('/:sid/test', async(req, res) => {
|
||||
SpeechCredential.ttsTestResult(sid, false);
|
||||
}
|
||||
}
|
||||
if (cred.use_for_stt) {
|
||||
if (cred.use_for_stt && api_key) {
|
||||
try {
|
||||
await testDeepgramStt(logger, {api_key});
|
||||
results.stt.status = 'ok';
|
||||
@@ -752,7 +761,7 @@ router.get('/:sid/test', async(req, res) => {
|
||||
*/
|
||||
|
||||
router.get('/speech/supportedLanguagesAndVoices', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
const {logger, getTtsVoices} = req.app.locals;
|
||||
try {
|
||||
const {vendor, label} = req.query;
|
||||
if (!vendor) {
|
||||
@@ -767,7 +776,7 @@ router.get('/speech/supportedLanguagesAndVoices', async(req, res) => {
|
||||
const tmp = credentials && credentials.length > 0 ? credentials[0] : null;
|
||||
const cred = tmp ? JSON.parse(decrypt(tmp.credential)) : null;
|
||||
try {
|
||||
const data = await getLanguagesAndVoicesForVendor(logger, vendor, cred);
|
||||
const data = await getLanguagesAndVoicesForVendor(logger, vendor, cred, getTtsVoices);
|
||||
res.status(200).json(data);
|
||||
} catch (err) {
|
||||
throw new DbErrorUnprocessableRequest(err.message);
|
||||
|
||||
@@ -382,11 +382,35 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
/login:
|
||||
post:
|
||||
tags:
|
||||
- Authentication
|
||||
summary: login and retrieve a JWT
|
||||
operationId: login
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Login'
|
||||
responses:
|
||||
200:
|
||||
description: user logged in
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessfulLogin'
|
||||
500:
|
||||
description: system error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
/logout:
|
||||
post:
|
||||
tags:
|
||||
- Authentication
|
||||
summary: log out and deactivate jwt
|
||||
summary: log out and deactivate the JWT
|
||||
operationId: logoutUser
|
||||
responses:
|
||||
204:
|
||||
@@ -584,10 +608,9 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type:
|
||||
array
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Users'
|
||||
$ref: '#/components/schemas/UserList'
|
||||
403:
|
||||
description: unauthorized
|
||||
500:
|
||||
@@ -610,27 +633,13 @@ paths:
|
||||
- Users
|
||||
summary: retrieve user information
|
||||
operationId: getUser
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
is_active:
|
||||
type: boolean
|
||||
force_change:
|
||||
type: boolean
|
||||
scope:
|
||||
type: string
|
||||
permissions:
|
||||
type: array
|
||||
responses:
|
||||
204:
|
||||
200:
|
||||
description: user information
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserProfile'
|
||||
403:
|
||||
description: user information
|
||||
content:
|
||||
@@ -674,6 +683,8 @@ paths:
|
||||
type: string
|
||||
permissions:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
204:
|
||||
description: user updated
|
||||
@@ -712,6 +723,8 @@ paths:
|
||||
type: string
|
||||
permissions:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
old_password:
|
||||
type: string
|
||||
description: existing password, which is to be replaced
|
||||
@@ -998,7 +1011,7 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
/AccountTest/:ServiceProviderSid:
|
||||
/AccountTest/{ServiceProviderSid}:
|
||||
parameters:
|
||||
- name: ServiceProviderSid
|
||||
in: path
|
||||
@@ -1095,6 +1108,9 @@ paths:
|
||||
requires_register:
|
||||
type: boolean
|
||||
description: wehther this provider requires us to send a REGISTER to them in order to receive calls
|
||||
register_use_tls:
|
||||
type: boolean
|
||||
description: wehther this provider requires us to send a REGISTER use TLS protocol
|
||||
register_username:
|
||||
type: string
|
||||
description: sip username to authenticate with, if registration is required
|
||||
@@ -1971,7 +1987,7 @@ paths:
|
||||
tags:
|
||||
- Service Providers
|
||||
summary: add a VoiPCarrier to a service provider based on PredefinedCarrier template
|
||||
operationId: createVoipCarrierFromTemplate
|
||||
operationId: createVoipCarrierFromTemplateBySP
|
||||
responses:
|
||||
201:
|
||||
description: voip carrier successfully created
|
||||
@@ -2079,6 +2095,12 @@ paths:
|
||||
summary: get supported languages, voices and models
|
||||
operationId: supportedLanguagesAndVoices
|
||||
parameters:
|
||||
- name: ServiceProviderSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: vendor
|
||||
in: query
|
||||
required: true
|
||||
@@ -2920,7 +2942,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: get a specific speech credential
|
||||
operationId: getSpeechCredential
|
||||
operationId: getSpeechCredentialByAccount
|
||||
responses:
|
||||
200:
|
||||
description: retrieve speech credentials for a specified account
|
||||
@@ -2934,7 +2956,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: update a speech credential
|
||||
operationId: updateSpeechCredential
|
||||
operationId: updateSpeechCredentialByAccount
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@@ -2955,7 +2977,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: delete a speech credential
|
||||
operationId: deleteSpeechCredential
|
||||
operationId: deleteSpeechCredentialByAccount
|
||||
responses:
|
||||
204:
|
||||
description: credential successfully deleted
|
||||
@@ -2966,8 +2988,14 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: get supported languages, voices and models
|
||||
operationId: supportedLanguagesAndVoices
|
||||
operationId: supportedLanguagesAndVoicesByAccount
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: vendor
|
||||
in: query
|
||||
required: true
|
||||
@@ -2995,7 +3023,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: test a speech credential
|
||||
operationId: testSpeechCredential
|
||||
operationId: testSpeechCredentialByAccount
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
@@ -3241,7 +3269,7 @@ paths:
|
||||
tags:
|
||||
- Service Providers
|
||||
summary: retrieve pcap for a call
|
||||
operationId: getRecentCallTrace
|
||||
operationId: getRecentCallTraceBySP
|
||||
responses:
|
||||
200:
|
||||
description: retrieve sip trace data
|
||||
@@ -3327,7 +3355,7 @@ paths:
|
||||
tags:
|
||||
- Service Providers
|
||||
summary: retrieve recent calls for an account
|
||||
operationId: listRecentCalls
|
||||
operationId: listRecentCallsBySP
|
||||
responses:
|
||||
200:
|
||||
description: retrieve recent call records for a specified account
|
||||
@@ -3428,7 +3456,7 @@ paths:
|
||||
tags:
|
||||
- Service Providers
|
||||
summary: retrieve sip trace detail for a call
|
||||
operationId: getRecentCallTrace
|
||||
operationId: getRecentCallTraceByCallId
|
||||
responses:
|
||||
200:
|
||||
description: retrieve sip trace data
|
||||
@@ -3455,7 +3483,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: retrieve pcap for a call
|
||||
operationId: getRecentCallTrace
|
||||
operationId: getRecentCallTraceByAccount
|
||||
responses:
|
||||
200:
|
||||
description: retrieve sip trace data
|
||||
@@ -3641,7 +3669,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: retrieve alerts for an account
|
||||
operationId: listAlerts
|
||||
operationId: listAlertsByAccount
|
||||
responses:
|
||||
200:
|
||||
description: retrieve alerts for a specified account
|
||||
@@ -4240,7 +4268,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: retrieve online sip users for an account
|
||||
operationId: listQueues
|
||||
operationId: listRegisteredSipUsers
|
||||
responses:
|
||||
200:
|
||||
description: retrieve online sip users for an account
|
||||
@@ -4254,7 +4282,7 @@ paths:
|
||||
tags:
|
||||
- Accounts
|
||||
summary: retrieve online sip users for an account by list of sip username
|
||||
operationId: listRegisteredSipUsers
|
||||
operationId: listRegisteredSipUsersByUsername
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@@ -4273,6 +4301,12 @@ paths:
|
||||
$ref: '#/components/schemas/RegisteredClient'
|
||||
/Accounts/{AccountSid}/RegisteredSipUsers/{Client}:
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: Client
|
||||
in: path
|
||||
required: true
|
||||
@@ -4293,6 +4327,13 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisteredClient'
|
||||
/Accounts/{AccountSid}/TtsCache/Synthesize:
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
post:
|
||||
tags:
|
||||
- Accounts
|
||||
@@ -5031,17 +5072,32 @@ components:
|
||||
scheme: bearer
|
||||
bearerFormat: token
|
||||
schemas:
|
||||
SuccessfulLogin:
|
||||
type: object
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
properties:
|
||||
token:
|
||||
type: string
|
||||
user_sid:
|
||||
type: string
|
||||
scope:
|
||||
type: string
|
||||
force_change:
|
||||
type: boolean
|
||||
|
||||
Login:
|
||||
type: object
|
||||
properties:
|
||||
user_sid:
|
||||
username:
|
||||
type: string
|
||||
api_token:
|
||||
type: string
|
||||
change_password:
|
||||
type: boolean
|
||||
password:
|
||||
type: string
|
||||
required:
|
||||
- user_sid
|
||||
- username
|
||||
- password
|
||||
|
||||
SuccessfulApiKeyAdd:
|
||||
type: object
|
||||
required:
|
||||
@@ -6096,8 +6152,23 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TtsModel'
|
||||
|
||||
|
||||
UserList:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
is_active:
|
||||
type: boolean
|
||||
force_change:
|
||||
type: boolean
|
||||
scope:
|
||||
type: string
|
||||
permissions:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
@@ -1,9 +1,14 @@
|
||||
module.exports = [
|
||||
{ name: 'Aurora English (US) Female', value: 'alpha-aurora-en-v2' },
|
||||
{ name: 'Asteria English (US) Female', value: 'alpha-asteria-en-v2' },
|
||||
{ name: 'Artemis English (UK) Female', value: 'alpha-artemis-en-v3' },
|
||||
{ name: 'Andromeda English (US) Female', value: 'alpha-andromeda-en-v3' },
|
||||
{ name: 'Stella English (UK) Female', value: 'alpha-stella-en-v2' },
|
||||
{ name: 'Orion English (US) Male', value: 'alpha-orion-en-v2' },
|
||||
{ name: 'Atlas English (US) Male', value: 'alpha-atlas-en-v3' },
|
||||
{ name: 'Asteria English (US) Female', value: 'aura-asteria-en' },
|
||||
{ name: 'Luna English (US) Female', value: 'aura-luna-en' },
|
||||
{ name: 'Stella English (US) Female', value: 'aura-stella-en' },
|
||||
{ name: 'Stella English (UK) Female', value: 'aura-athena-en' },
|
||||
{ name: 'Hera English (US) Female', value: 'aura-hera-en' },
|
||||
{ name: 'Orion English (US) Male', value: 'aura-orion-en' },
|
||||
{ name: 'Arcas English (US) Male', value: 'aura-arcas-en' },
|
||||
{ name: 'Perseus English (US) Male', value: 'aura-perseus-en' },
|
||||
{ name: 'Angus English (Ireland) Male', value: 'aura-angus-en' },
|
||||
{ name: 'Orpheus English (US) Male', value: 'aura-orpheus-en' },
|
||||
{ name: 'Helios English (UK) Male', value: 'aura-helios-en' },
|
||||
{ name: 'Zeus English (US) Male', value: 'aura-zeus-en' },
|
||||
];
|
||||
|
||||
@@ -263,9 +263,7 @@ const testDeepgramTTS = async(logger, synthAudio, credentials) => {
|
||||
{
|
||||
vendor: 'deepgram',
|
||||
credentials,
|
||||
language: 'en-US',
|
||||
voice: 'alpha-aurora-en-v2',
|
||||
model: 'alpha-aurora-en-v2',
|
||||
model: 'aura-asteria-en',
|
||||
text: 'Hi there and welcome to jambones!'
|
||||
}
|
||||
);
|
||||
@@ -406,6 +404,8 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
|
||||
else if ('deepgram' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
|
||||
obj.deepgram_stt_uri = o.deepgram_stt_uri;
|
||||
obj.deepgram_stt_use_tls = o.deepgram_stt_use_tls;
|
||||
}
|
||||
else if ('ibm' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
@@ -464,49 +464,90 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
|
||||
]
|
||||
}
|
||||
*/
|
||||
async function getLanguagesAndVoicesForVendor(logger, vendor, credential) {
|
||||
async function getLanguagesAndVoicesForVendor(logger, vendor, credential, getTtsVoices) {
|
||||
switch (vendor) {
|
||||
case 'google':
|
||||
return await getLanguagesVoicesForGoogle(credential);
|
||||
return await getLanguagesVoicesForGoogle(credential, getTtsVoices, logger);
|
||||
case 'aws':
|
||||
return await getLanguagesVoicesForAws(credential);
|
||||
return await getLanguagesVoicesForAws(credential, getTtsVoices, logger);
|
||||
case 'microsoft':
|
||||
return await getLanguagesVoicesForMicrosoft(credential);
|
||||
return await getLanguagesVoicesForMicrosoft(credential, getTtsVoices, logger);
|
||||
case 'wellsaid':
|
||||
return await getLanguagesVoicesForWellsaid(credential);
|
||||
return await getLanguagesVoicesForWellsaid(credential, getTtsVoices, logger);
|
||||
case 'nuance':
|
||||
return await getLanguagesVoicesForNuane(credential);
|
||||
return await getLanguagesVoicesForNuane(credential, getTtsVoices, logger);
|
||||
case 'deepgram':
|
||||
return await getLanguagesVoicesForDeepgram(credential);
|
||||
return await getLanguagesVoicesForDeepgram(credential, getTtsVoices, logger);
|
||||
case 'ibm':
|
||||
return await getLanguagesVoicesForIbm(credential);
|
||||
return await getLanguagesVoicesForIbm(credential, getTtsVoices, logger);
|
||||
case 'nvidia':
|
||||
return await getLanguagesVoicesForNvida(credential);
|
||||
return await getLanguagesVoicesForNvida(credential, getTtsVoices, logger);
|
||||
case 'cobalt':
|
||||
return await getLanguagesVoicesForCobalt(credential);
|
||||
return await getLanguagesVoicesForCobalt(credential, getTtsVoices, logger);
|
||||
case 'soniox':
|
||||
return await getLanguagesVoicesForSoniox(credential);
|
||||
return await getLanguagesVoicesForSoniox(credential, getTtsVoices, logger);
|
||||
case 'elevenlabs':
|
||||
return await getLanguagesVoicesForElevenlabs(credential);
|
||||
return await getLanguagesVoicesForElevenlabs(credential, getTtsVoices, logger);
|
||||
case 'assemblyai':
|
||||
return await getLanguagesVoicesForAssemblyAI(credential);
|
||||
return await getLanguagesVoicesForAssemblyAI(credential, getTtsVoices, logger);
|
||||
case 'whisper':
|
||||
return await getLanguagesVoicesForWhisper(credential);
|
||||
return await getLanguagesVoicesForWhisper(credential, getTtsVoices, logger);
|
||||
default:
|
||||
logger.info(`invalid vendor ${vendor}, return empty result`);
|
||||
throw new Error(`Invalid vendor ${vendor}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getLanguagesVoicesForGoogle(credential) {
|
||||
async function getLanguagesVoicesForGoogle(credential, getTtsVoices, logger) {
|
||||
if (credential) {
|
||||
try {
|
||||
const [result] = await getTtsVoices({
|
||||
vendor: 'google',
|
||||
credentials: credential
|
||||
});
|
||||
const tts = parseGooglelanguagesVoices(result.voices);
|
||||
return tranform(tts, SttGoogleLanguagesVoices);
|
||||
} catch (err) {
|
||||
logger.info('Error while fetching google languages, voices, return predefined values', err);
|
||||
}
|
||||
}
|
||||
return tranform(TtsGoogleLanguagesVoices, SttGoogleLanguagesVoices);
|
||||
}
|
||||
|
||||
async function getLanguagesVoicesForAws(credential) {
|
||||
async function getLanguagesVoicesForAws(credential, getTtsVoices, logger) {
|
||||
if (credential) {
|
||||
try {
|
||||
const result = await getTtsVoices({
|
||||
vendor: 'aws',
|
||||
credentials: {
|
||||
accessKeyId: credential.access_key_id,
|
||||
secretAccessKey: credential.secret_access_key,
|
||||
region: credential.aws_region || process.env.AWS_REGION
|
||||
}
|
||||
});
|
||||
const tts = parseAwsLanguagesVoices(result.Voices);
|
||||
return tranform(tts, SttAwsLanguagesVoices);
|
||||
} catch (err) {
|
||||
logger.info('Error while fetching AWS languages, voices, return predefined values', err);
|
||||
}
|
||||
}
|
||||
return tranform(TtsAwsLanguagesVoices, SttAwsLanguagesVoices);
|
||||
}
|
||||
|
||||
async function getLanguagesVoicesForMicrosoft(credential) {
|
||||
async function getLanguagesVoicesForMicrosoft(credential, getTtsVoices, logger) {
|
||||
if (credential) {
|
||||
try {
|
||||
const get = bent('https://westus.tts.speech.microsoft.com', 'GET', 'json', {
|
||||
'Ocp-Apim-Subscription-Key' : credential.api_key
|
||||
});
|
||||
|
||||
const voices = await get('/cognitiveservices/voices/list');
|
||||
const tts = parseMicrosoftLanguagesVoices(voices);
|
||||
return tranform(tts, SttMicrosoftLanguagesVoices);
|
||||
} catch (err) {
|
||||
logger.info('Error while fetching Microsoft languages, voices, return predefined values', err);
|
||||
}
|
||||
}
|
||||
return tranform(TtsMicrosoftLanguagesVoices, SttMicrosoftLanguagesVoices);
|
||||
}
|
||||
|
||||
@@ -514,7 +555,19 @@ async function getLanguagesVoicesForWellsaid(credential) {
|
||||
return tranform(TtsWellsaidLanguagesVoices);
|
||||
}
|
||||
|
||||
async function getLanguagesVoicesForNuane(credential) {
|
||||
async function getLanguagesVoicesForNuane(credential, getTtsVoices, logger) {
|
||||
if (credential) {
|
||||
try {
|
||||
const result = await getTtsVoices({
|
||||
vendor: 'nuance',
|
||||
credentials: credential
|
||||
});
|
||||
const tts = parseNuanceLanguagesVoices(result.result.voices);
|
||||
return tranform(tts, SttNuanceLanguagesVoices);
|
||||
} catch (err) {
|
||||
logger.info('Error while fetching IBM languages, voices, return predefined values', err);
|
||||
}
|
||||
}
|
||||
return tranform(TtsNuanceLanguagesVoices, SttNuanceLanguagesVoices);
|
||||
}
|
||||
|
||||
@@ -522,7 +575,19 @@ async function getLanguagesVoicesForDeepgram(credential) {
|
||||
return tranform(undefined, SttDeepgramLanguagesVoices, TtsModelDeepgram);
|
||||
}
|
||||
|
||||
async function getLanguagesVoicesForIbm(credential) {
|
||||
async function getLanguagesVoicesForIbm(credential, getTtsVoices, logger) {
|
||||
if (credential) {
|
||||
try {
|
||||
const result = await getTtsVoices({
|
||||
vendor: 'ibm',
|
||||
credentials: credential
|
||||
});
|
||||
const tts = parseIBMLanguagesVoices(result.result.voices);
|
||||
return tranform(tts, SttIbmLanguagesVoices);
|
||||
} catch (err) {
|
||||
logger.info('Error while fetching IBM languages, voices, return predefined values', err);
|
||||
}
|
||||
}
|
||||
return tranform(TtsIbmLanguagesVoices, SttIbmLanguagesVoices);
|
||||
}
|
||||
|
||||
@@ -596,6 +661,125 @@ function tranform(tts, stt, models) {
|
||||
};
|
||||
}
|
||||
|
||||
function parseGooglelanguagesVoices(data) {
|
||||
return data.reduce((acc, voice) => {
|
||||
const languageCode = voice.languageCodes[0];
|
||||
const existingLanguage = acc.find((lang) => lang.value === languageCode);
|
||||
|
||||
if (existingLanguage) {
|
||||
existingLanguage.voices.push({
|
||||
value: voice.name,
|
||||
name: `${voice.name.substring(languageCode.length + 1, voice.name.length)} (${voice.ssmlGender})`
|
||||
});
|
||||
} else {
|
||||
acc.push({
|
||||
value: languageCode,
|
||||
name: SttGoogleLanguagesVoices.find((lang) => lang.value === languageCode)?.name || languageCode,
|
||||
voices: [{
|
||||
value: voice.name,
|
||||
name: `${voice.name.substring(languageCode.length + 1, voice.name.length)} (${voice.ssmlGender})`
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function parseIBMLanguagesVoices(data) {
|
||||
return data.reduce((acc, voice) => {
|
||||
const languageCode = voice.language;
|
||||
const existingLanguage = acc.find((lang) => lang.value === languageCode);
|
||||
if (existingLanguage) {
|
||||
existingLanguage.voices.push({
|
||||
value: voice.name,
|
||||
name: `(${voice.gender}) ${voice.description}`
|
||||
});
|
||||
} else {
|
||||
acc.push({
|
||||
value: languageCode,
|
||||
name: SttGoogleLanguagesVoices.find((lang) => lang.value === languageCode)?.name || languageCode,
|
||||
voices: [{
|
||||
value: voice.name,
|
||||
name: `(${voice.gender}) ${voice.description}`
|
||||
}]
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function parseAwsLanguagesVoices(data) {
|
||||
return data.reduce((acc, voice) => {
|
||||
const languageCode = voice.LanguageCode;
|
||||
const existingLanguage = acc.find((lang) => lang.value === languageCode);
|
||||
if (existingLanguage) {
|
||||
existingLanguage.voices.push({
|
||||
value: voice.Id,
|
||||
name: `(${voice.Gender}) ${voice.Name}`
|
||||
});
|
||||
} else {
|
||||
acc.push({
|
||||
value: languageCode,
|
||||
name: voice.LanguageName,
|
||||
voices: [{
|
||||
value: voice.Id,
|
||||
name: `(${voice.Gender}) ${voice.Name}`
|
||||
}]
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function parseNuanceLanguagesVoices(data) {
|
||||
return data.reduce((acc, voice) => {
|
||||
const languageCode = voice.language;
|
||||
const existingLanguage = acc.find((lang) => lang.value === languageCode);
|
||||
if (existingLanguage) {
|
||||
existingLanguage.voices.push({
|
||||
value: voice.name,
|
||||
name: voice.name,
|
||||
model: voice.model
|
||||
});
|
||||
} else {
|
||||
acc.push({
|
||||
value: languageCode,
|
||||
name: SttGoogleLanguagesVoices.find((lang) => lang.value === languageCode)?.name || languageCode,
|
||||
voices: [{
|
||||
value: voice.name,
|
||||
name: voice.name,
|
||||
model: voice.model
|
||||
}]
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function parseMicrosoftLanguagesVoices(data) {
|
||||
return data.reduce((acc, voice) => {
|
||||
const languageCode = voice.Locale;
|
||||
const existingLanguage = acc.find((lang) => lang.value === languageCode);
|
||||
if (existingLanguage) {
|
||||
existingLanguage.voices.push({
|
||||
value: voice.ShortName,
|
||||
name: `${voice.DisplayName} (${voice.Gender})`,
|
||||
});
|
||||
} else {
|
||||
acc.push({
|
||||
value: voice.Locale,
|
||||
name: voice.LocaleName,
|
||||
voices: [{
|
||||
value: voice.ShortName,
|
||||
name: `${voice.DisplayName} (${voice.Gender})`,
|
||||
}]
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testGoogleTts,
|
||||
testGoogleStt,
|
||||
|
||||
15934
package-lock.json
generated
15934
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
67
package.json
67
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jambonz-api-server",
|
||||
"version": "0.8.5",
|
||||
"version": "0.9.0",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
@@ -19,53 +19,54 @@
|
||||
"url": "https://github.com/jambonz/jambonz-api-server.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.363.0",
|
||||
"@aws-sdk/client-transcribe": "^3.363.0",
|
||||
"@azure/storage-blob": "^12.15.0",
|
||||
"@deepgram/sdk": "^1.21.0",
|
||||
"@google-cloud/speech": "^5.2.0",
|
||||
"@google-cloud/storage": "^6.12.0",
|
||||
"@jambonz/db-helpers": "^0.9.1",
|
||||
"@aws-sdk/client-s3": "^3.550.0",
|
||||
"@aws-sdk/client-transcribe": "^3.549.0",
|
||||
"@azure/storage-blob": "^12.17.0",
|
||||
"@deepgram/sdk": "^3.2.0",
|
||||
"@google-cloud/speech": "^6.5.0",
|
||||
"@google-cloud/storage": "^7.9.0",
|
||||
"@jambonz/db-helpers": "^0.9.3",
|
||||
"@jambonz/lamejs": "^1.2.2",
|
||||
"@jambonz/mw-registrar": "^0.2.7",
|
||||
"@jambonz/realtimedb-helpers": "^0.8.7",
|
||||
"@jambonz/speech-utils": "^0.0.33",
|
||||
"@jambonz/realtimedb-helpers": "^0.8.8",
|
||||
"@jambonz/speech-utils": "^0.0.49",
|
||||
"@jambonz/time-series": "^0.2.8",
|
||||
"@jambonz/verb-specifications": "^0.0.45",
|
||||
"@soniox/soniox-node": "^1.1.1",
|
||||
"argon2": "^0.30.3",
|
||||
"assemblyai": "^3.0.1",
|
||||
"@jambonz/verb-specifications": "^0.0.69",
|
||||
"@soniox/soniox-node": "^1.2.2",
|
||||
"argon2": "^0.40.1",
|
||||
"assemblyai": "^4.3.4",
|
||||
"bent": "^7.3.12",
|
||||
"cors": "^2.8.5",
|
||||
"debug": "^4.3.4",
|
||||
"express": "^4.18.1",
|
||||
"express-rate-limit": "^6.4.0",
|
||||
"form-data": "^2.5.1",
|
||||
"helmet": "^5.1.0",
|
||||
"ibm-watson": "^7.1.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"mailgun.js": "^9.1.2",
|
||||
"microsoft-cognitiveservices-speech-sdk": "1.31.0",
|
||||
"mysql2": "^2.3.3",
|
||||
"nocache": "3.0.4",
|
||||
"passport": "^0.6.0",
|
||||
"express": "^4.19.2",
|
||||
"express-rate-limit": "^7.2.0",
|
||||
"form-data": "^4.0.0",
|
||||
"helmet": "^7.1.0",
|
||||
"ibm-watson": "^9.0.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mailgun.js": "^10.2.1",
|
||||
"microsoft-cognitiveservices-speech-sdk": "1.36.0",
|
||||
"mysql2": "^3.9.3",
|
||||
"nocache": "4.0.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
"pino": "^5.17.0",
|
||||
"short-uuid": "^4.1.0",
|
||||
"stripe": "^8.222.0",
|
||||
"swagger-ui-express": "^4.4.0",
|
||||
"uuid": "^8.3.2",
|
||||
"pino": "^8.20.0",
|
||||
"short-uuid": "^4.2.2",
|
||||
"stream-buffers": "^3.0.2",
|
||||
"stripe": "^14.24.0",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"uuid": "^9.0.1",
|
||||
"wav": "^1.0.2",
|
||||
"ws": "^8.12.1",
|
||||
"ws": "^8.16.0",
|
||||
"yamljs": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"husky": "7.0.4",
|
||||
"husky": "9.0.11",
|
||||
"nyc": "^15.1.0",
|
||||
"request": "^2.88.2",
|
||||
"request-promise-native": "^1.0.9",
|
||||
"tape": "^5.5.3"
|
||||
"tape": "^5.7.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,6 +292,60 @@ test('speech credentials tests', async(t) => {
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
|
||||
}
|
||||
// test create deepgram onprem
|
||||
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
body: {
|
||||
vendor: 'deepgram',
|
||||
use_for_stt: true,
|
||||
deepgram_stt_uri: "127.0.0.1:50002",
|
||||
deepgram_stt_use_tls: true
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully added speech credential for deepgram');
|
||||
const dg_sid = result.body.sid;
|
||||
|
||||
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
//console.log(JSON.stringify(result));
|
||||
t.ok(result.statusCode === 200, 'successfully get speech credential for deepgram');
|
||||
t.ok(result.body.deepgram_stt_uri === '127.0.0.1:50002', "deepgram_stt_uri is correct for deepgram");
|
||||
t.ok(result.body.deepgram_stt_use_tls === true, "deepgram_stt_use_tls is correct for deepgram");
|
||||
|
||||
result = await request.put(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
body: {
|
||||
vendor: 'deepgram',
|
||||
use_for_stt: true,
|
||||
deepgram_stt_uri: "127.0.0.2:50002",
|
||||
deepgram_stt_use_tls: false
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully updated speech credential for deepgram onprem');
|
||||
|
||||
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
//console.log(JSON.stringify(result));
|
||||
t.ok(result.statusCode === 200, 'successfully get speech credential for deepgram onprem');
|
||||
t.ok(result.body.deepgram_stt_uri === '127.0.0.2:50002', "deepgram_stt_uri is correct for deepgram onprem");
|
||||
t.ok(result.body.deepgram_stt_use_tls === false, "deepgram_stt_use_tls is correct for deepgram onprem");
|
||||
|
||||
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
|
||||
auth: authUser,
|
||||
resolveWithFullResponse: true,
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully deleted speech credential for deepgram onprem');
|
||||
|
||||
/* add a credential for ibm tts */
|
||||
if (process.env.IBM_TTS_API_KEY && process.env.IBM_TTS_REGION) {
|
||||
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||
|
||||
Reference in New Issue
Block a user