echogarden 1.1.0 → 1.2.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/data/schemas/options.json +56 -1
- package/dist/alignment/DTWSequenceAlignmentWindowed.js +6 -1
- package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
- package/dist/alignment/SpeechAlignment.js +1 -1
- package/dist/alignment/SpeechAlignment.js.map +1 -1
- package/dist/api/APIOptions.d.ts +3 -0
- package/dist/api/Alignment.js +2 -1
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/GlobalOptions.d.ts +10 -0
- package/dist/api/GlobalOptions.js +21 -3
- package/dist/api/GlobalOptions.js.map +1 -1
- package/dist/cli/CLI.js +224 -191
- package/dist/cli/CLI.js.map +1 -1
- package/dist/cli/CLIConfigFile.js +7 -7
- package/dist/cli/CLIConfigFile.js.map +1 -1
- package/dist/cli/CLIOptions.d.ts +7 -0
- package/dist/cli/CLIOptions.js +2 -0
- package/dist/cli/CLIOptions.js.map +1 -0
- package/dist/cli/CLIParser.d.ts +5 -6
- package/dist/cli/CLIParser.js +16 -12
- package/dist/cli/CLIParser.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +34 -17
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/utilities/FileDownloader.js +3 -1
- package/dist/utilities/FileDownloader.js.map +1 -1
- package/dist/utilities/Logger.d.ts +5 -4
- package/dist/utilities/Logger.js +19 -8
- package/dist/utilities/Logger.js.map +1 -1
- package/dist/utilities/PackageManager.js +3 -2
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +1 -1
- package/dist/utilities/Utilities.js +9 -9
- package/dist/utilities/Utilities.js.map +1 -1
- package/docs/API.md +3 -10
- package/docs/CLI.md +88 -77
- package/docs/Contributing.md +4 -4
- package/docs/Engines.md +1 -1
- package/docs/Options.md +25 -2
- package/docs/Releases.md +8 -8
- package/docs/Tasklist.md +17 -19
- package/docs/Technical.md +2 -2
- package/package.json +3 -3
- package/src/alignment/DTWSequenceAlignmentWindowed.ts +7 -1
- package/src/alignment/SpeechAlignment.ts +1 -1
- package/src/api/APIOptions.ts +3 -0
- package/src/api/Alignment.ts +4 -2
- package/src/api/GlobalOptions.ts +33 -5
- package/src/cli/CLI.ts +257 -205
- package/src/cli/CLIConfigFile.ts +7 -7
- package/src/cli/CLIOptions.ts +8 -0
- package/src/cli/CLIParser.ts +20 -17
- package/src/recognition/WhisperSTT.ts +38 -17
- package/src/utilities/FileDownloader.ts +5 -1
- package/src/utilities/Logger.ts +22 -8
- package/src/utilities/PackageManager.ts +4 -3
- package/src/utilities/Utilities.ts +9 -9
package/dist/cli/CLI.js
CHANGED
|
@@ -23,6 +23,7 @@ import { startServer } from '../server/Server.js';
|
|
|
23
23
|
import { OpenPromise } from '../utilities/OpenPromise.js';
|
|
24
24
|
import JSON5 from 'json5';
|
|
25
25
|
import { getLowercaseFileExtension, resolveToModuleRootDir } from '../utilities/PathUtilities.js';
|
|
26
|
+
import { CLIOptionsKeys } from './CLIOptions.js';
|
|
26
27
|
//const log = logToStderr
|
|
27
28
|
async function startIfInWorkerThread() {
|
|
28
29
|
if (isMainThread || !parentPort) {
|
|
@@ -46,81 +47,122 @@ async function startIfInWorkerThread() {
|
|
|
46
47
|
}
|
|
47
48
|
export async function start(processArgs) {
|
|
48
49
|
const logger = new Logger();
|
|
49
|
-
|
|
50
|
+
const operationData = {
|
|
51
|
+
operation: '',
|
|
52
|
+
operationArgs: [],
|
|
53
|
+
globalOptions: {},
|
|
54
|
+
cliOptions: {},
|
|
55
|
+
operationOptionsLookup: new Map(),
|
|
56
|
+
};
|
|
50
57
|
try {
|
|
51
58
|
const packageData = await readAndParseJsonFile(resolveToModuleRootDir('package.json'));
|
|
52
59
|
logger.log(chalk.magentaBright(`Echogarden v${packageData.version}\n`));
|
|
53
|
-
const
|
|
54
|
-
if (!
|
|
55
|
-
logger.log(`Supported operations:\n\n${
|
|
60
|
+
const operation = processArgs[0];
|
|
61
|
+
if (!operation || operation == 'help') {
|
|
62
|
+
logger.log(`Supported operations:\n\n${help.join('\n')}`);
|
|
56
63
|
process.exit(0);
|
|
57
64
|
}
|
|
58
|
-
if (
|
|
59
|
-
logger.log(`There's no
|
|
60
|
-
process.exit(
|
|
65
|
+
if (operation == '--help' || operation == '-h') {
|
|
66
|
+
logger.log(`There's no operation called '${operation}'. Did you mean to run 'echogarden help'?`);
|
|
67
|
+
process.exit(1);
|
|
61
68
|
}
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
if (operation.startsWith('-')) {
|
|
70
|
+
logger.log(`Operation name '${operation}' is invalid. It cannot start with a hyphen.`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const { operationArgs, parsedArgumentsLookup } = parseCLIArguments(processArgs.slice(1));
|
|
74
|
+
if (!parsedArgumentsLookup.has('config')) {
|
|
64
75
|
const defaultConfigFile = `./${appName}.config`;
|
|
65
76
|
const defaultJsonConfigFile = defaultConfigFile + '.json';
|
|
66
77
|
if (existsSync(defaultConfigFile)) {
|
|
67
|
-
|
|
78
|
+
parsedArgumentsLookup.set('config', defaultConfigFile);
|
|
68
79
|
}
|
|
69
80
|
else if (existsSync(defaultJsonConfigFile)) {
|
|
70
|
-
|
|
81
|
+
parsedArgumentsLookup.set('config', defaultJsonConfigFile);
|
|
71
82
|
}
|
|
72
83
|
}
|
|
73
|
-
if (
|
|
74
|
-
const configFilePath =
|
|
75
|
-
|
|
76
|
-
let
|
|
84
|
+
if (parsedArgumentsLookup.has('config')) {
|
|
85
|
+
const configFilePath = parsedArgumentsLookup.get('config');
|
|
86
|
+
parsedArgumentsLookup.delete('config');
|
|
87
|
+
let parsedConfigFile;
|
|
77
88
|
if (configFilePath.endsWith('.config')) {
|
|
78
|
-
|
|
89
|
+
parsedConfigFile = await parseConfigFile(configFilePath);
|
|
79
90
|
}
|
|
80
91
|
else if (configFilePath.endsWith('.config.json')) {
|
|
81
|
-
|
|
92
|
+
parsedConfigFile = await parseJSONConfigFile(configFilePath);
|
|
82
93
|
}
|
|
83
94
|
else {
|
|
84
95
|
throw new Error(`Specified config file '${configFilePath}' doesn't have a supported extension. Should be either '.config' or '.config.json'`);
|
|
85
96
|
}
|
|
86
|
-
let sectionName =
|
|
97
|
+
let sectionName = operation;
|
|
87
98
|
if (sectionName.startsWith('speak-')) {
|
|
88
99
|
sectionName = 'speak';
|
|
89
100
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
const globalOptionsLookup = new Map();
|
|
102
|
+
const cliOptionsLookup = new Map();
|
|
103
|
+
const operationsOptionsLookup = new Map();
|
|
104
|
+
if (parsedConfigFile.has('global')) {
|
|
105
|
+
for (const [key, value] of parsedConfigFile.get('global')) {
|
|
106
|
+
globalOptionsLookup.set(key, value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (parsedConfigFile.has('cli')) {
|
|
110
|
+
for (const [key, value] of parsedConfigFile.get('cli')) {
|
|
111
|
+
cliOptionsLookup.set(key, value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (parsedConfigFile.has(sectionName)) {
|
|
115
|
+
for (const [key, value] of parsedConfigFile.get(sectionName)) {
|
|
116
|
+
operationsOptionsLookup.set(key, value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const globalOptionsKeys = API.listGlobalOptions();
|
|
120
|
+
const cliOptionsKeys = CLIOptionsKeys;
|
|
121
|
+
for (const [key, value] of parsedArgumentsLookup) {
|
|
122
|
+
if (globalOptionsKeys.includes(key)) {
|
|
123
|
+
globalOptionsLookup.set(key, value);
|
|
124
|
+
}
|
|
125
|
+
else if (cliOptionsKeys.includes(key)) {
|
|
126
|
+
cliOptionsLookup.set(key, value);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
operationsOptionsLookup.set(key, value);
|
|
130
|
+
}
|
|
93
131
|
}
|
|
94
|
-
|
|
132
|
+
operationData.operation = operation;
|
|
133
|
+
operationData.operationArgs = operationArgs;
|
|
134
|
+
operationData.globalOptions = await optionsLookupToTypedObject(globalOptionsLookup, 'GlobalOptions');
|
|
135
|
+
operationData.cliOptions = await optionsLookupToTypedObject(cliOptionsLookup, 'CLIOptions');
|
|
136
|
+
operationData.operationOptionsLookup = operationsOptionsLookup;
|
|
95
137
|
}
|
|
96
138
|
}
|
|
97
139
|
catch (e) {
|
|
98
140
|
resetActiveLogger();
|
|
99
|
-
logger.logTitledMessage(`Error`, e.message, chalk.redBright);
|
|
141
|
+
logger.logTitledMessage(`Error`, e.message, chalk.redBright, 'error');
|
|
100
142
|
process.exit(1);
|
|
101
143
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
debugMode = true;
|
|
144
|
+
for (const key in operationData.globalOptions) {
|
|
145
|
+
const value = operationData.globalOptions[key];
|
|
146
|
+
API.setGlobalOption(key, value);
|
|
106
147
|
}
|
|
148
|
+
const debugMode = operationData.cliOptions.debug || false;
|
|
107
149
|
try {
|
|
108
|
-
await startWithArgs(
|
|
150
|
+
await startWithArgs(operationData);
|
|
109
151
|
}
|
|
110
152
|
catch (e) {
|
|
111
153
|
resetActiveLogger();
|
|
112
154
|
if (debugMode) {
|
|
113
|
-
logger.log(e);
|
|
155
|
+
logger.log(e, 'error');
|
|
114
156
|
}
|
|
115
157
|
else {
|
|
116
|
-
logger.logTitledMessage(`Error`, e.message, chalk.redBright);
|
|
158
|
+
logger.logTitledMessage(`Error`, e.message, chalk.redBright, 'error');
|
|
117
159
|
}
|
|
118
160
|
process.exit(1);
|
|
119
161
|
}
|
|
120
162
|
process.exit(0);
|
|
121
163
|
}
|
|
122
164
|
const executableName = `${chalk.cyanBright('echogarden')}`;
|
|
123
|
-
const
|
|
165
|
+
const help = [
|
|
124
166
|
`${executableName} ${chalk.magentaBright('speak')} text [output files...] [options...]`,
|
|
125
167
|
` Speak the given text\n`,
|
|
126
168
|
`${executableName} ${chalk.magentaBright('speak-file')} inputFile [output files...] [options...]`,
|
|
@@ -160,103 +202,103 @@ const commandHelp = [
|
|
|
160
202
|
`${executableName} ${chalk.magentaBright('serve')} [options...]`,
|
|
161
203
|
` Start a server\n`,
|
|
162
204
|
`Options reference: ${chalk.blueBright('https://bit.ly/echogarden-options')}`
|
|
163
|
-
//` ${chalk.blueBright('https://github.com/echogarden-project/echogarden/blob/main/docs/Options.md')}`
|
|
164
205
|
];
|
|
165
|
-
async function startWithArgs(
|
|
206
|
+
async function startWithArgs(operationData) {
|
|
166
207
|
const logger = new Logger();
|
|
167
|
-
switch (
|
|
208
|
+
switch (operationData.operation) {
|
|
168
209
|
case 'speak':
|
|
169
210
|
case 'speak-file':
|
|
170
211
|
case 'speak-url':
|
|
171
212
|
case 'speak-wikipedia': {
|
|
172
|
-
await speak(
|
|
213
|
+
await speak(operationData);
|
|
173
214
|
break;
|
|
174
215
|
}
|
|
175
216
|
case 'transcribe': {
|
|
176
|
-
await transcribe(
|
|
217
|
+
await transcribe(operationData);
|
|
177
218
|
break;
|
|
178
219
|
}
|
|
179
220
|
case 'align': {
|
|
180
|
-
await align(
|
|
221
|
+
await align(operationData);
|
|
181
222
|
break;
|
|
182
223
|
}
|
|
183
224
|
case 'translate-speech': {
|
|
184
|
-
await translateSpeech(
|
|
225
|
+
await translateSpeech(operationData);
|
|
185
226
|
break;
|
|
186
227
|
}
|
|
187
228
|
case 'align-translation': {
|
|
188
|
-
await alignTranslation(
|
|
229
|
+
await alignTranslation(operationData);
|
|
189
230
|
break;
|
|
190
231
|
}
|
|
191
232
|
case 'detect-language': {
|
|
192
|
-
await detectLanguage(
|
|
233
|
+
await detectLanguage(operationData, 'auto');
|
|
193
234
|
break;
|
|
194
235
|
}
|
|
195
236
|
case 'detect-speech-language': {
|
|
196
|
-
await detectLanguage(
|
|
237
|
+
await detectLanguage(operationData, 'speech');
|
|
197
238
|
break;
|
|
198
239
|
}
|
|
199
240
|
case 'detect-text-language': {
|
|
200
|
-
await detectLanguage(
|
|
241
|
+
await detectLanguage(operationData, 'text');
|
|
201
242
|
break;
|
|
202
243
|
}
|
|
203
244
|
case 'detect-voice-activity': {
|
|
204
|
-
await detectVoiceActivity(
|
|
245
|
+
await detectVoiceActivity(operationData);
|
|
205
246
|
break;
|
|
206
247
|
}
|
|
207
248
|
case 'denoise': {
|
|
208
|
-
await denoise(
|
|
249
|
+
await denoise(operationData);
|
|
209
250
|
break;
|
|
210
251
|
}
|
|
211
252
|
case 'isolate': {
|
|
212
|
-
await isolate(
|
|
253
|
+
await isolate(operationData);
|
|
213
254
|
break;
|
|
214
255
|
}
|
|
215
256
|
case 'list-engines': {
|
|
216
|
-
await listEngines(
|
|
257
|
+
await listEngines(operationData);
|
|
217
258
|
break;
|
|
218
259
|
}
|
|
219
260
|
case 'list-voices': {
|
|
220
|
-
await listTTSVoices(
|
|
261
|
+
await listTTSVoices(operationData);
|
|
221
262
|
break;
|
|
222
263
|
}
|
|
223
264
|
case 'install': {
|
|
224
|
-
await installPackages(
|
|
265
|
+
await installPackages(operationData);
|
|
225
266
|
break;
|
|
226
267
|
}
|
|
227
268
|
case 'uninstall': {
|
|
228
|
-
await uninstallPackages(
|
|
269
|
+
await uninstallPackages(operationData);
|
|
229
270
|
break;
|
|
230
271
|
}
|
|
231
272
|
case 'list-packages': {
|
|
232
|
-
await listPackages(
|
|
273
|
+
await listPackages(operationData);
|
|
233
274
|
break;
|
|
234
275
|
}
|
|
235
276
|
case 'serve': {
|
|
236
|
-
await serve(
|
|
277
|
+
await serve(operationData);
|
|
237
278
|
break;
|
|
238
279
|
}
|
|
239
280
|
default: {
|
|
240
|
-
logger.logTitledMessage(`Unknown
|
|
281
|
+
logger.logTitledMessage(`Unknown operation`, operationData.operation, chalk.redBright, 'error');
|
|
241
282
|
process.exit(1);
|
|
242
283
|
}
|
|
243
284
|
}
|
|
244
285
|
}
|
|
245
|
-
async function speak(
|
|
286
|
+
async function speak(operationData) {
|
|
246
287
|
const logger = new Logger();
|
|
247
|
-
const
|
|
248
|
-
const
|
|
288
|
+
const { operationArgs, operation, operationOptionsLookup, cliOptions } = operationData;
|
|
289
|
+
const mainArg = operationArgs[0];
|
|
290
|
+
const outputFilenames = operationArgs.slice(1);
|
|
249
291
|
if (mainArg == undefined) {
|
|
250
|
-
if (
|
|
292
|
+
if (operation == 'speak') {
|
|
251
293
|
throw new Error(`'speak' requires an argument containing the text to speak.`);
|
|
252
294
|
}
|
|
253
|
-
else if (
|
|
295
|
+
else if (operation == 'speak-file') {
|
|
254
296
|
throw new Error(`'speak-file' requires an argument containing the file to speak.`);
|
|
255
297
|
}
|
|
256
|
-
else if (
|
|
298
|
+
else if (operation == 'speak-url') {
|
|
257
299
|
throw new Error(`'speak-url' requires an argument containing the url to speak.`);
|
|
258
300
|
}
|
|
259
|
-
else if (
|
|
301
|
+
else if (operation == 'speak-wikipedia') {
|
|
260
302
|
throw new Error(`'speak-wikipedia' requires an argument containing the name of the Wikipedia article to speak.`);
|
|
261
303
|
}
|
|
262
304
|
return;
|
|
@@ -264,17 +306,17 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
264
306
|
const additionalOptionsSchema = new Map();
|
|
265
307
|
additionalOptionsSchema.set('play', { type: 'boolean' });
|
|
266
308
|
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
267
|
-
if (
|
|
268
|
-
cliOptions.
|
|
309
|
+
if (cliOptions.play == null) {
|
|
310
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
269
311
|
}
|
|
270
|
-
const options = await
|
|
271
|
-
const allowOverwrite = getWithDefault(
|
|
312
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SynthesisOptions', additionalOptionsSchema);
|
|
313
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
272
314
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
273
315
|
let plainText = undefined;
|
|
274
316
|
let textSegments;
|
|
275
317
|
const plainTextParagraphBreaks = options.plainText?.paragraphBreaks || API.defaultSynthesisOptions.plainText.paragraphBreaks;
|
|
276
318
|
const plainTextWhitespace = options.plainText?.whitespace || API.defaultSynthesisOptions.plainText.whitespace;
|
|
277
|
-
if (
|
|
319
|
+
if (operation == 'speak') {
|
|
278
320
|
if (options.ssml) {
|
|
279
321
|
textSegments = [mainArg];
|
|
280
322
|
}
|
|
@@ -283,7 +325,7 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
283
325
|
}
|
|
284
326
|
plainText = mainArg;
|
|
285
327
|
}
|
|
286
|
-
else if (
|
|
328
|
+
else if (operation == 'speak-file') {
|
|
287
329
|
const sourceFile = mainArg;
|
|
288
330
|
if (!existsSync(sourceFile)) {
|
|
289
331
|
throw new Error(`The given source file '${sourceFile}' was not found.`);
|
|
@@ -313,9 +355,9 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
313
355
|
throw new Error(`'speak-file' only supports inputs with extensions 'txt', 'html', 'htm', 'xml', 'ssml', 'srt', 'vtt'`);
|
|
314
356
|
}
|
|
315
357
|
}
|
|
316
|
-
else if (
|
|
358
|
+
else if (operation == 'speak-url') {
|
|
317
359
|
if (options.ssml) {
|
|
318
|
-
throw new Error(`speak-url doesn't
|
|
360
|
+
throw new Error(`speak-url doesn't accept SSML inputs`);
|
|
319
361
|
}
|
|
320
362
|
const url = mainArg;
|
|
321
363
|
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
@@ -325,7 +367,7 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
325
367
|
const textContent = await fetchDocumentText(url);
|
|
326
368
|
textSegments = splitToParagraphs(textContent, 'single', 'preserve');
|
|
327
369
|
}
|
|
328
|
-
else if (
|
|
370
|
+
else if (operation == 'speak-wikipedia') {
|
|
329
371
|
if (options.ssml) {
|
|
330
372
|
throw new Error(`speak-wikipedia doesn't provide SSML inputs`);
|
|
331
373
|
}
|
|
@@ -336,7 +378,7 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
336
378
|
textSegments = await parseWikipediaArticle(mainArg, getShortLanguageCode(options.language));
|
|
337
379
|
}
|
|
338
380
|
else {
|
|
339
|
-
throw new Error(
|
|
381
|
+
throw new Error(`Invalid operation specified: '${operation}'`);
|
|
340
382
|
}
|
|
341
383
|
async function onSegment(segmentData) {
|
|
342
384
|
if (includesPlaceholderPattern) {
|
|
@@ -344,7 +386,7 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
344
386
|
}
|
|
345
387
|
await writeOutputFilesForSegment(outputFilenames, segmentData.index, segmentData.total, segmentData.audio, segmentData.timeline, segmentData.transcript, segmentData.language, allowOverwrite);
|
|
346
388
|
logger.end();
|
|
347
|
-
if (
|
|
389
|
+
if (cliOptions.play) {
|
|
348
390
|
let gainAmount = -3 - segmentData.peakDecibelsSoFar;
|
|
349
391
|
//gainAmount = Math.min(gainAmount, 0)
|
|
350
392
|
const audioWithAddedGain = applyGainDecibels(segmentData.audio, gainAmount);
|
|
@@ -372,24 +414,22 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
372
414
|
}
|
|
373
415
|
logger.end();
|
|
374
416
|
}
|
|
375
|
-
async function transcribe(
|
|
417
|
+
async function transcribe(operationData) {
|
|
376
418
|
const logger = new Logger();
|
|
377
|
-
const
|
|
378
|
-
const
|
|
419
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
420
|
+
const sourceFilename = operationArgs[0];
|
|
421
|
+
const outputFilenames = operationArgs.slice(1);
|
|
379
422
|
if (sourceFilename == undefined) {
|
|
380
423
|
throw new Error(`'transcribe' requires an argument containing the source file name.`);
|
|
381
424
|
}
|
|
382
425
|
if (!existsSync(sourceFilename)) {
|
|
383
426
|
throw new Error(`The given source audio file '${sourceFilename}' was not found.`);
|
|
384
427
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
388
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
389
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`);
|
|
428
|
+
if (cliOptions.play == null) {
|
|
429
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
390
430
|
}
|
|
391
|
-
const options = await
|
|
392
|
-
const allowOverwrite = getWithDefault(
|
|
431
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'RecognitionOptions');
|
|
432
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
393
433
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
394
434
|
const { transcript, timeline, wordTimeline, language, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.recognize(sourceFilename, options);
|
|
395
435
|
if (outputFilenames.length > 0) {
|
|
@@ -405,7 +445,7 @@ async function transcribe(commandArgs, cliOptions) {
|
|
|
405
445
|
await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
|
|
406
446
|
}
|
|
407
447
|
logger.end();
|
|
408
|
-
if (
|
|
448
|
+
if (cliOptions.play) {
|
|
409
449
|
let audioToPlay;
|
|
410
450
|
if (isolatedRawAudio) {
|
|
411
451
|
audioToPlay = isolatedRawAudio;
|
|
@@ -417,17 +457,18 @@ async function transcribe(commandArgs, cliOptions) {
|
|
|
417
457
|
await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
|
|
418
458
|
}
|
|
419
459
|
}
|
|
420
|
-
async function align(
|
|
460
|
+
async function align(operationData) {
|
|
421
461
|
const logger = new Logger();
|
|
422
|
-
const
|
|
423
|
-
const
|
|
462
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
463
|
+
const audioFilename = operationArgs[0];
|
|
464
|
+
const outputFilenames = operationArgs.slice(2);
|
|
424
465
|
if (audioFilename == undefined) {
|
|
425
466
|
throw new Error(`align requires an argument containing the audio file path.`);
|
|
426
467
|
}
|
|
427
468
|
if (!existsSync(audioFilename)) {
|
|
428
469
|
throw new Error(`The given source file '${audioFilename}' was not found.`);
|
|
429
470
|
}
|
|
430
|
-
const alignmentReferenceFile =
|
|
471
|
+
const alignmentReferenceFile = operationArgs[1];
|
|
431
472
|
if (alignmentReferenceFile == undefined) {
|
|
432
473
|
throw new Error(`align requires a second argument containing the alignment reference file path.`);
|
|
433
474
|
}
|
|
@@ -449,14 +490,11 @@ async function align(commandArgs, cliOptions) {
|
|
|
449
490
|
else {
|
|
450
491
|
throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
|
|
451
492
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
455
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
456
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`);
|
|
493
|
+
if (cliOptions.play == null) {
|
|
494
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
457
495
|
}
|
|
458
|
-
const options = await
|
|
459
|
-
const allowOverwrite = getWithDefault(
|
|
496
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'AlignmentOptions');
|
|
497
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
460
498
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
461
499
|
const { timeline, wordTimeline, transcript, language, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.align(audioFilename, text, options);
|
|
462
500
|
if (outputFilenames.length > 0) {
|
|
@@ -480,7 +518,7 @@ async function align(commandArgs, cliOptions) {
|
|
|
480
518
|
await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
|
|
481
519
|
}
|
|
482
520
|
logger.end();
|
|
483
|
-
if (
|
|
521
|
+
if (cliOptions.play) {
|
|
484
522
|
let audioToPlay;
|
|
485
523
|
if (isolatedRawAudio) {
|
|
486
524
|
audioToPlay = isolatedRawAudio;
|
|
@@ -492,17 +530,18 @@ async function align(commandArgs, cliOptions) {
|
|
|
492
530
|
await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
|
|
493
531
|
}
|
|
494
532
|
}
|
|
495
|
-
async function alignTranslation(
|
|
533
|
+
async function alignTranslation(operationData) {
|
|
496
534
|
const logger = new Logger();
|
|
497
|
-
const
|
|
498
|
-
const
|
|
535
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
536
|
+
const audioFilename = operationArgs[0];
|
|
537
|
+
const outputFilenames = operationArgs.slice(2);
|
|
499
538
|
if (audioFilename == undefined) {
|
|
500
539
|
throw new Error(`align-translation requires an argument containing the audio file path.`);
|
|
501
540
|
}
|
|
502
541
|
if (!existsSync(audioFilename)) {
|
|
503
542
|
throw new Error(`The given source file '${audioFilename}' was not found.`);
|
|
504
543
|
}
|
|
505
|
-
const alignmentReferenceFile =
|
|
544
|
+
const alignmentReferenceFile = operationArgs[1];
|
|
506
545
|
if (alignmentReferenceFile == undefined) {
|
|
507
546
|
throw new Error(`align-translation requires a second argument containing the translated reference file path.`);
|
|
508
547
|
}
|
|
@@ -524,14 +563,11 @@ async function alignTranslation(commandArgs, cliOptions) {
|
|
|
524
563
|
else {
|
|
525
564
|
throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
|
|
526
565
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
530
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
531
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`);
|
|
566
|
+
if (cliOptions.play == null) {
|
|
567
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
532
568
|
}
|
|
533
|
-
const options = await
|
|
534
|
-
const allowOverwrite = getWithDefault(
|
|
569
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TranslationAlignmentOptions');
|
|
570
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
535
571
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
536
572
|
const { timeline, wordTimeline, transcript, language, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.alignTranslation(audioFilename, text, options);
|
|
537
573
|
if (outputFilenames.length > 0) {
|
|
@@ -555,7 +591,7 @@ async function alignTranslation(commandArgs, cliOptions) {
|
|
|
555
591
|
await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
|
|
556
592
|
}
|
|
557
593
|
logger.end();
|
|
558
|
-
if (
|
|
594
|
+
if (cliOptions.play) {
|
|
559
595
|
let audioToPlay;
|
|
560
596
|
if (isolatedRawAudio) {
|
|
561
597
|
audioToPlay = isolatedRawAudio;
|
|
@@ -567,24 +603,22 @@ async function alignTranslation(commandArgs, cliOptions) {
|
|
|
567
603
|
await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
|
|
568
604
|
}
|
|
569
605
|
}
|
|
570
|
-
async function translateSpeech(
|
|
606
|
+
async function translateSpeech(operationData) {
|
|
571
607
|
const logger = new Logger();
|
|
572
|
-
const
|
|
573
|
-
const
|
|
608
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
609
|
+
const inputFilename = operationArgs[0];
|
|
610
|
+
const outputFilenames = operationArgs.slice(1);
|
|
574
611
|
if (inputFilename == undefined) {
|
|
575
612
|
throw new Error(`translate-speech requires an argument containing the input file path.`);
|
|
576
613
|
}
|
|
577
614
|
if (!existsSync(inputFilename)) {
|
|
578
615
|
throw new Error(`The given input file '${inputFilename}' was not found.`);
|
|
579
616
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
583
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
584
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`);
|
|
617
|
+
if (cliOptions.play == null) {
|
|
618
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
585
619
|
}
|
|
586
|
-
const options = await
|
|
587
|
-
const allowOverwrite = getWithDefault(
|
|
620
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SpeechTranslationOptions');
|
|
621
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
588
622
|
await checkOutputFilenames(outputFilenames, true, true, true);
|
|
589
623
|
const { transcript, timeline, wordTimeline, sourceLanguage, targetLanguage, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.translateSpeech(inputFilename, options);
|
|
590
624
|
if (outputFilenames.length > 0) {
|
|
@@ -600,7 +634,7 @@ async function translateSpeech(commandArgs, cliOptions) {
|
|
|
600
634
|
await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
|
|
601
635
|
}
|
|
602
636
|
logger.end();
|
|
603
|
-
if (
|
|
637
|
+
if (cliOptions.play) {
|
|
604
638
|
let audioToPlay;
|
|
605
639
|
if (isolatedRawAudio) {
|
|
606
640
|
audioToPlay = isolatedRawAudio;
|
|
@@ -632,15 +666,14 @@ async function translateSpeech(commandArgs, cliOptions) {
|
|
|
632
666
|
await playAudioWithWordTimeline(normalizedAudioToPlay, timelineToPlay, transcriptToPlay);
|
|
633
667
|
}
|
|
634
668
|
}
|
|
635
|
-
async function detectLanguage(
|
|
669
|
+
async function detectLanguage(operationData, mode) {
|
|
636
670
|
const logger = new Logger();
|
|
637
|
-
const
|
|
638
|
-
const
|
|
671
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
672
|
+
const inputFilePath = operationArgs[0];
|
|
673
|
+
const outputFilenames = operationArgs.slice(1);
|
|
639
674
|
if (!existsSync(inputFilePath)) {
|
|
640
675
|
throw new Error(`The given input file '${inputFilePath}' was not found.`);
|
|
641
676
|
}
|
|
642
|
-
const additionalOptionsSchema = new Map();
|
|
643
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
644
677
|
const inputFileExtension = getLowercaseFileExtension(inputFilePath);
|
|
645
678
|
const supportedInputTextFormats = ['txt', 'srt', 'vtt'];
|
|
646
679
|
let results;
|
|
@@ -652,8 +685,8 @@ async function detectLanguage(commandArgs, cliOptions, mode) {
|
|
|
652
685
|
if (!supportedInputTextFormats.includes(inputFileExtension)) {
|
|
653
686
|
throw new Error(`'detect-text-language' doesn't support input file extension '${inputFileExtension}'`);
|
|
654
687
|
}
|
|
655
|
-
const options = await
|
|
656
|
-
allowOverwrite = getWithDefault(
|
|
688
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TextLanguageDetectionOptions');
|
|
689
|
+
allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
657
690
|
await checkOutputFilenames(outputFilenames, false, true, false);
|
|
658
691
|
let text = await readFile(inputFilePath, { encoding: 'utf-8' });
|
|
659
692
|
if (inputFileExtension == 'srt' || inputFileExtension == 'vtt') {
|
|
@@ -666,8 +699,8 @@ async function detectLanguage(commandArgs, cliOptions, mode) {
|
|
|
666
699
|
if (inputFilePath == undefined) {
|
|
667
700
|
throw new Error(`detect-speech-language requires an argument containing the input audio file path.`);
|
|
668
701
|
}
|
|
669
|
-
const options = await
|
|
670
|
-
allowOverwrite = getWithDefault(
|
|
702
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SpeechLanguageDetectionOptions');
|
|
703
|
+
allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
671
704
|
await checkOutputFilenames(outputFilenames, false, true, false);
|
|
672
705
|
const { detectedLanguage, detectedLanguageProbabilities } = await API.detectSpeechLanguage(inputFilePath, options);
|
|
673
706
|
results = detectedLanguageProbabilities;
|
|
@@ -682,29 +715,27 @@ async function detectLanguage(commandArgs, cliOptions, mode) {
|
|
|
682
715
|
}
|
|
683
716
|
else {
|
|
684
717
|
const resultsAsText = results.slice(0, 10).map(result => `${formatLanguageCodeWithName(result.language)}: ${result.probability.toFixed(5)}`).join('\n');
|
|
685
|
-
logger.log('');
|
|
686
|
-
logger.log(resultsAsText);
|
|
718
|
+
logger.log('', 'output');
|
|
719
|
+
logger.log(resultsAsText, 'output');
|
|
687
720
|
}
|
|
688
721
|
logger.end();
|
|
689
722
|
}
|
|
690
|
-
async function detectVoiceActivity(
|
|
723
|
+
async function detectVoiceActivity(operationData) {
|
|
691
724
|
const logger = new Logger();
|
|
692
|
-
const
|
|
693
|
-
const
|
|
725
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
726
|
+
const audioFilename = operationArgs[0];
|
|
727
|
+
const outputFilenames = operationArgs.slice(1);
|
|
694
728
|
if (audioFilename == undefined) {
|
|
695
729
|
throw new Error(`detect-voice-activity requires an argument containing the audio file path.`);
|
|
696
730
|
}
|
|
697
731
|
if (!existsSync(audioFilename)) {
|
|
698
732
|
throw new Error(`The given source audio file '${audioFilename}' was not found.`);
|
|
699
733
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
703
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
704
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`);
|
|
734
|
+
if (cliOptions.play == null) {
|
|
735
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
705
736
|
}
|
|
706
|
-
const options = await
|
|
707
|
-
const allowOverwrite = getWithDefault(
|
|
737
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'VADOptions');
|
|
738
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
708
739
|
await checkOutputFilenames(outputFilenames, true, true, true);
|
|
709
740
|
let { timeline, verboseTimeline, inputRawAudio, croppedRawAudio } = await API.detectVoiceActivity(audioFilename, options);
|
|
710
741
|
if (outputFilenames.length > 0) {
|
|
@@ -726,7 +757,7 @@ async function detectVoiceActivity(commandArgs, cliOptions) {
|
|
|
726
757
|
}
|
|
727
758
|
}
|
|
728
759
|
logger.end();
|
|
729
|
-
if (
|
|
760
|
+
if (cliOptions.play) {
|
|
730
761
|
const normalizedAudio = normalizeAudioLevel(inputRawAudio);
|
|
731
762
|
const timelineToPlay = verboseTimeline.map(entry => {
|
|
732
763
|
return { ...entry, type: 'word' };
|
|
@@ -734,24 +765,22 @@ async function detectVoiceActivity(commandArgs, cliOptions) {
|
|
|
734
765
|
await playAudioWithWordTimeline(normalizedAudio, timelineToPlay);
|
|
735
766
|
}
|
|
736
767
|
}
|
|
737
|
-
async function denoise(
|
|
768
|
+
async function denoise(operationData) {
|
|
738
769
|
const logger = new Logger();
|
|
739
|
-
const
|
|
740
|
-
const
|
|
770
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
771
|
+
const audioFilename = operationArgs[0];
|
|
772
|
+
const outputFilenames = operationArgs.slice(1);
|
|
741
773
|
if (audioFilename == undefined) {
|
|
742
774
|
throw new Error(`'denoise' requires an argument containing the audio file path.`);
|
|
743
775
|
}
|
|
744
776
|
if (!existsSync(audioFilename)) {
|
|
745
777
|
throw new Error(`The given source audio file '${audioFilename}' was not found.`);
|
|
746
778
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
750
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
751
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`);
|
|
779
|
+
if (cliOptions.play == null) {
|
|
780
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
752
781
|
}
|
|
753
|
-
const options = await
|
|
754
|
-
const allowOverwrite = getWithDefault(
|
|
782
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'DenoisingOptions');
|
|
783
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
755
784
|
await checkOutputFilenames(outputFilenames, true, false, false);
|
|
756
785
|
const { denoisedAudio } = await API.denoise(audioFilename, options);
|
|
757
786
|
if (outputFilenames.length > 0) {
|
|
@@ -762,28 +791,26 @@ async function denoise(commandArgs, cliOptions) {
|
|
|
762
791
|
await fileSaver(denoisedAudio, [], '');
|
|
763
792
|
}
|
|
764
793
|
logger.end();
|
|
765
|
-
if (
|
|
794
|
+
if (cliOptions.play) {
|
|
766
795
|
await playAudioSamples(denoisedAudio);
|
|
767
796
|
}
|
|
768
797
|
}
|
|
769
|
-
async function isolate(
|
|
798
|
+
async function isolate(operationData) {
|
|
770
799
|
const logger = new Logger();
|
|
771
|
-
const
|
|
772
|
-
const
|
|
800
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
801
|
+
const audioFilename = operationArgs[0];
|
|
802
|
+
const outputFilenames = operationArgs.slice(1);
|
|
773
803
|
if (audioFilename == undefined) {
|
|
774
804
|
throw new Error(`'isolate' requires an argument containing the audio file path.`);
|
|
775
805
|
}
|
|
776
806
|
if (!existsSync(audioFilename)) {
|
|
777
807
|
throw new Error(`The given source audio file '${audioFilename}' was not found.`);
|
|
778
808
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
782
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
783
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`);
|
|
809
|
+
if (cliOptions.play == null) {
|
|
810
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
784
811
|
}
|
|
785
|
-
const options = await
|
|
786
|
-
const allowOverwrite = getWithDefault(
|
|
812
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SourceSeparationOptions');
|
|
813
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
787
814
|
await checkOutputFilenames(outputFilenames, true, false, false);
|
|
788
815
|
const { inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.isolate(audioFilename, options);
|
|
789
816
|
if (outputFilenames.length > 0) {
|
|
@@ -793,15 +820,16 @@ async function isolate(commandArgs, cliOptions) {
|
|
|
793
820
|
await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, false);
|
|
794
821
|
}
|
|
795
822
|
logger.end();
|
|
796
|
-
if (
|
|
823
|
+
if (cliOptions.play) {
|
|
797
824
|
await playAudioSamples(isolatedRawAudio);
|
|
798
825
|
}
|
|
799
826
|
}
|
|
800
|
-
async function listEngines(
|
|
827
|
+
async function listEngines(operationData) {
|
|
801
828
|
const logger = new Logger();
|
|
802
|
-
const
|
|
829
|
+
const { operationArgs } = operationData;
|
|
830
|
+
const targetOperation = operationArgs[0];
|
|
803
831
|
if (!targetOperation) {
|
|
804
|
-
throw new Error(`The 'list-engines'
|
|
832
|
+
throw new Error(`The 'list-engines' operation requires an argument specifying the operation to list engines for, like 'echogarden list-engines transcribe'.`);
|
|
805
833
|
}
|
|
806
834
|
let engines;
|
|
807
835
|
switch (targetOperation) {
|
|
@@ -867,19 +895,20 @@ async function listEngines(commandArgs, cliOptions) {
|
|
|
867
895
|
}
|
|
868
896
|
}
|
|
869
897
|
for (const [index, engine] of engines.entries()) {
|
|
870
|
-
logger.logTitledMessage('Identifier', chalk.magentaBright(engine.id));
|
|
871
|
-
logger.logTitledMessage('Name', engine.name);
|
|
872
|
-
logger.logTitledMessage('Description', engine.description);
|
|
873
|
-
logger.logTitledMessage('Type', engine.type);
|
|
898
|
+
logger.logTitledMessage('Identifier', chalk.magentaBright(engine.id), undefined, 'output');
|
|
899
|
+
logger.logTitledMessage('Name', engine.name, undefined, 'output');
|
|
900
|
+
logger.logTitledMessage('Description', engine.description, undefined, 'output');
|
|
901
|
+
logger.logTitledMessage('Type', engine.type, undefined, 'output');
|
|
874
902
|
if (index < engines.length - 1) {
|
|
875
|
-
logger.log('');
|
|
903
|
+
logger.log('', 'output');
|
|
876
904
|
}
|
|
877
905
|
}
|
|
878
906
|
}
|
|
879
|
-
async function listTTSVoices(
|
|
907
|
+
async function listTTSVoices(operationData) {
|
|
880
908
|
const logger = new Logger();
|
|
881
|
-
const
|
|
882
|
-
const
|
|
909
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
910
|
+
const targetEngine = operationArgs[0];
|
|
911
|
+
const outputFilenames = operationArgs.slice(1);
|
|
883
912
|
if (!targetEngine) {
|
|
884
913
|
const optionsSchema = await getOptionsSchema();
|
|
885
914
|
const { enum: ttsEnginesEnum } = getOptionTypeFromSchema(['VoiceListRequestOptions', 'engine'], optionsSchema);
|
|
@@ -887,9 +916,9 @@ async function listTTSVoices(commandArgs, cliOptions) {
|
|
|
887
916
|
}
|
|
888
917
|
const additionalOptionsSchema = new Map();
|
|
889
918
|
additionalOptionsSchema.set('overwrite', { type: 'boolean' });
|
|
890
|
-
|
|
891
|
-
const options = await
|
|
892
|
-
const allowOverwrite = getWithDefault(
|
|
919
|
+
operationOptionsLookup.set('engine', targetEngine);
|
|
920
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'VoiceListRequestOptions');
|
|
921
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
893
922
|
await checkOutputFilenames(outputFilenames, false, true, false);
|
|
894
923
|
const { voiceList } = await API.requestVoiceList(options);
|
|
895
924
|
const voiceListText = voiceList.map(entry => {
|
|
@@ -913,56 +942,58 @@ async function listTTSVoices(commandArgs, cliOptions) {
|
|
|
913
942
|
}
|
|
914
943
|
}
|
|
915
944
|
else {
|
|
916
|
-
logger.log(voiceListText);
|
|
945
|
+
logger.log(voiceListText, 'output');
|
|
917
946
|
}
|
|
918
947
|
logger.end();
|
|
919
948
|
}
|
|
920
|
-
async function installPackages(
|
|
949
|
+
async function installPackages(operationData) {
|
|
921
950
|
const logger = new Logger();
|
|
922
|
-
|
|
951
|
+
const { operationArgs } = operationData;
|
|
952
|
+
if (operationArgs.length == 0) {
|
|
923
953
|
throw new Error('No package names specified');
|
|
924
954
|
}
|
|
925
955
|
const failedPackageNames = [];
|
|
926
|
-
for (const packageName of
|
|
956
|
+
for (const packageName of operationArgs) {
|
|
927
957
|
try {
|
|
928
958
|
await loadPackage(packageName);
|
|
929
959
|
}
|
|
930
960
|
catch (e) {
|
|
931
961
|
resetActiveLogger();
|
|
932
|
-
logger.
|
|
962
|
+
logger.logTitledMessage(`Failed installing package ${packageName}`, e, chalk.redBright, 'error');
|
|
933
963
|
failedPackageNames.push(packageName);
|
|
934
964
|
}
|
|
935
965
|
}
|
|
936
966
|
if (failedPackageNames.length > 0) {
|
|
937
967
|
if (failedPackageNames.length == 1) {
|
|
938
|
-
logger.log(`The package ${failedPackageNames[0]} failed to install
|
|
968
|
+
logger.log(`The package ${failedPackageNames[0]} failed to install`, 'error');
|
|
939
969
|
}
|
|
940
970
|
else {
|
|
941
|
-
logger.log(`The packages ${failedPackageNames.join(', ')} failed to install
|
|
971
|
+
logger.log(`The packages ${failedPackageNames.join(', ')} failed to install`, 'error');
|
|
942
972
|
}
|
|
943
973
|
}
|
|
944
974
|
}
|
|
945
|
-
async function uninstallPackages(
|
|
975
|
+
async function uninstallPackages(operationData) {
|
|
946
976
|
const logger = new Logger();
|
|
947
|
-
|
|
977
|
+
const { operationArgs } = operationData;
|
|
978
|
+
if (operationArgs.length == 0) {
|
|
948
979
|
throw new Error('No package names specified');
|
|
949
980
|
}
|
|
950
981
|
const failedPackageNames = [];
|
|
951
|
-
for (const packageName of
|
|
982
|
+
for (const packageName of operationArgs) {
|
|
952
983
|
try {
|
|
953
984
|
await removePackage(packageName);
|
|
954
985
|
}
|
|
955
986
|
catch (e) {
|
|
956
987
|
resetActiveLogger();
|
|
957
|
-
logger.
|
|
988
|
+
logger.logTitledMessage(`Failed uninstalling package ${packageName}`, e, chalk.redBright, 'error');
|
|
958
989
|
failedPackageNames.push(packageName);
|
|
959
990
|
}
|
|
960
991
|
}
|
|
961
992
|
if (failedPackageNames.length > 0) {
|
|
962
|
-
logger.log(`The packages ${failedPackageNames.join(', ')} failed to uninstall
|
|
993
|
+
logger.log(`The packages ${failedPackageNames.join(', ')} failed to uninstall`, 'error');
|
|
963
994
|
}
|
|
964
995
|
}
|
|
965
|
-
async function listPackages(
|
|
996
|
+
async function listPackages(operationData) {
|
|
966
997
|
const logger = new Logger();
|
|
967
998
|
const packagesDir = await ensureAndGetPackagesDir();
|
|
968
999
|
const installedPackageNames = await readdir(packagesDir);
|
|
@@ -981,11 +1012,13 @@ async function listPackages(commandArgs, cliOptions) {
|
|
|
981
1012
|
}
|
|
982
1013
|
});
|
|
983
1014
|
installedPackageNamesFormatted.sort();
|
|
984
|
-
logger.log(`Total of ${installedPackageNamesFormatted.length} packages installed in '${packagesDir}'
|
|
1015
|
+
logger.log(`Total of ${installedPackageNamesFormatted.length} packages installed in '${packagesDir}'`);
|
|
1016
|
+
logger.log(``);
|
|
985
1017
|
logger.log(installedPackageNamesFormatted.join('\n'));
|
|
986
1018
|
}
|
|
987
|
-
async function serve(
|
|
988
|
-
const
|
|
1019
|
+
async function serve(operationData) {
|
|
1020
|
+
const { operationOptionsLookup } = operationData;
|
|
1021
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'ServerOptions');
|
|
989
1022
|
async function onServerStarted(serverOptions) {
|
|
990
1023
|
// Run a test routine (early development)
|
|
991
1024
|
//await runClientWebSocketTest(serverOptions.port!, serverOptions.secure!)
|
|
@@ -1009,7 +1042,7 @@ async function writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAu
|
|
|
1009
1042
|
}
|
|
1010
1043
|
}
|
|
1011
1044
|
}
|
|
1012
|
-
async function
|
|
1045
|
+
async function optionsLookupToTypedObject(cliOptionsMap, optionsRoot, additionalOptionsSchema) {
|
|
1013
1046
|
const optionsSchema = await getOptionsSchema();
|
|
1014
1047
|
const resultingObj = {};
|
|
1015
1048
|
function setValueAtPath(path, value) {
|