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/src/cli/CLI.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as API from '../api/API.js'
|
|
2
|
-
import {
|
|
3
|
-
import { convertHtmlToText, formatIntegerWithLeadingZeros, formatListWithQuotedElements, getWithDefault, logToStderr, setupUnhandledExceptionListeners, splitFilenameOnExtendedExtension, stringifyAndFormatJson } from '../utilities/Utilities.js'
|
|
2
|
+
import { parseCLIArguments } from './CLIParser.js'
|
|
3
|
+
import { clip, convertHtmlToText, formatIntegerWithLeadingZeros, formatListWithQuotedElements, getWithDefault, logToStderr, setupUnhandledExceptionListeners, splitFilenameOnExtendedExtension, stringifyAndFormatJson } from '../utilities/Utilities.js'
|
|
4
4
|
import { getOptionTypeFromSchema, SchemaTypeDefinition } from './CLIOptionsSchema.js'
|
|
5
5
|
import { ParsedConfigFile, parseConfigFile, parseJSONConfigFile } from './CLIConfigFile.js'
|
|
6
6
|
|
|
@@ -25,6 +25,7 @@ import { ServerOptions, startServer } from '../server/Server.js'
|
|
|
25
25
|
import { OpenPromise } from '../utilities/OpenPromise.js'
|
|
26
26
|
import JSON5 from 'json5'
|
|
27
27
|
import { getLowercaseFileExtension, resolveToModuleRootDir } from '../utilities/PathUtilities.js'
|
|
28
|
+
import { CLIOptions, CLIOptionsKeys } from './CLIOptions.js'
|
|
28
29
|
|
|
29
30
|
//const log = logToStderr
|
|
30
31
|
|
|
@@ -56,92 +57,150 @@ async function startIfInWorkerThread() {
|
|
|
56
57
|
start(process.argv.slice(2))
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
type CLIOperationData = {
|
|
61
|
+
operation: string
|
|
62
|
+
operationArgs: string[]
|
|
63
|
+
|
|
64
|
+
globalOptions: API.GlobalOptions
|
|
65
|
+
cliOptions: CLIOptions
|
|
66
|
+
|
|
67
|
+
operationOptionsLookup: Map<string, string>
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
export async function start(processArgs: string[]) {
|
|
60
71
|
const logger = new Logger()
|
|
61
72
|
|
|
62
|
-
|
|
73
|
+
const operationData: CLIOperationData = {
|
|
74
|
+
operation: '',
|
|
75
|
+
operationArgs: [],
|
|
76
|
+
|
|
77
|
+
globalOptions: {},
|
|
78
|
+
cliOptions: {},
|
|
79
|
+
|
|
80
|
+
operationOptionsLookup: new Map<string, string>(),
|
|
81
|
+
}
|
|
63
82
|
|
|
64
83
|
try {
|
|
65
84
|
const packageData = await readAndParseJsonFile(resolveToModuleRootDir('package.json'))
|
|
66
85
|
|
|
67
86
|
logger.log(chalk.magentaBright(`Echogarden v${packageData.version}\n`))
|
|
68
87
|
|
|
69
|
-
const
|
|
88
|
+
const operation = processArgs[0]
|
|
70
89
|
|
|
71
|
-
if (!
|
|
72
|
-
logger.log(`Supported operations:\n\n${
|
|
90
|
+
if (!operation || operation == 'help') {
|
|
91
|
+
logger.log(`Supported operations:\n\n${help.join('\n')}`)
|
|
73
92
|
process.exit(0)
|
|
74
93
|
}
|
|
75
94
|
|
|
76
|
-
if (
|
|
77
|
-
logger.log(`There's no
|
|
78
|
-
process.exit(
|
|
95
|
+
if (operation == '--help' || operation == '-h') {
|
|
96
|
+
logger.log(`There's no operation called '${operation}'. Did you mean to run 'echogarden help'?`)
|
|
97
|
+
process.exit(1)
|
|
79
98
|
}
|
|
80
99
|
|
|
81
|
-
|
|
100
|
+
if (operation.startsWith('-')) {
|
|
101
|
+
logger.log(`Operation name '${operation}' is invalid. It cannot start with a hyphen.`)
|
|
102
|
+
process.exit(1)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { operationArgs, parsedArgumentsLookup } = parseCLIArguments(processArgs.slice(1))
|
|
82
106
|
|
|
83
|
-
if (!
|
|
107
|
+
if (!parsedArgumentsLookup.has('config')) {
|
|
84
108
|
const defaultConfigFile = `./${appName}.config`
|
|
85
109
|
const defaultJsonConfigFile = defaultConfigFile + '.json'
|
|
86
110
|
|
|
87
111
|
if (existsSync(defaultConfigFile)) {
|
|
88
|
-
|
|
112
|
+
parsedArgumentsLookup.set('config', defaultConfigFile)
|
|
89
113
|
} else if (existsSync(defaultJsonConfigFile)) {
|
|
90
|
-
|
|
114
|
+
parsedArgumentsLookup.set('config', defaultJsonConfigFile)
|
|
91
115
|
}
|
|
92
116
|
}
|
|
93
117
|
|
|
94
|
-
if (
|
|
95
|
-
const configFilePath =
|
|
96
|
-
|
|
118
|
+
if (parsedArgumentsLookup.has('config')) {
|
|
119
|
+
const configFilePath = parsedArgumentsLookup.get('config')!
|
|
120
|
+
parsedArgumentsLookup.delete('config')
|
|
97
121
|
|
|
98
|
-
let
|
|
122
|
+
let parsedConfigFile: ParsedConfigFile
|
|
99
123
|
|
|
100
124
|
if (configFilePath.endsWith('.config')) {
|
|
101
|
-
|
|
125
|
+
parsedConfigFile = await parseConfigFile(configFilePath)
|
|
102
126
|
} else if (configFilePath.endsWith('.config.json')) {
|
|
103
|
-
|
|
127
|
+
parsedConfigFile = await parseJSONConfigFile(configFilePath)
|
|
104
128
|
} else {
|
|
105
129
|
throw new Error(`Specified config file '${configFilePath}' doesn't have a supported extension. Should be either '.config' or '.config.json'`)
|
|
106
130
|
}
|
|
107
131
|
|
|
108
|
-
let sectionName =
|
|
132
|
+
let sectionName = operation
|
|
109
133
|
|
|
110
134
|
if (sectionName.startsWith('speak-')) {
|
|
111
135
|
sectionName = 'speak'
|
|
112
136
|
}
|
|
113
137
|
|
|
114
|
-
const
|
|
138
|
+
const globalOptionsLookup = new Map<string, string>()
|
|
139
|
+
const cliOptionsLookup = new Map<string, string>()
|
|
140
|
+
const operationsOptionsLookup = new Map<string, string>()
|
|
141
|
+
|
|
142
|
+
if (parsedConfigFile.has('global')) {
|
|
143
|
+
for (const [key, value] of parsedConfigFile.get('global')!) {
|
|
144
|
+
globalOptionsLookup.set(key, value)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
115
147
|
|
|
116
|
-
|
|
117
|
-
|
|
148
|
+
if (parsedConfigFile.has('cli')) {
|
|
149
|
+
for (const [key, value] of parsedConfigFile.get('cli')!) {
|
|
150
|
+
cliOptionsLookup.set(key, value)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (parsedConfigFile.has(sectionName)) {
|
|
155
|
+
for (const [key, value] of parsedConfigFile.get(sectionName)!) {
|
|
156
|
+
operationsOptionsLookup.set(key, value)
|
|
157
|
+
}
|
|
118
158
|
}
|
|
119
159
|
|
|
120
|
-
|
|
160
|
+
const globalOptionsKeys = API.listGlobalOptions()
|
|
161
|
+
const cliOptionsKeys = CLIOptionsKeys
|
|
162
|
+
|
|
163
|
+
for (const [key, value] of parsedArgumentsLookup) {
|
|
164
|
+
if (globalOptionsKeys.includes(key)) {
|
|
165
|
+
globalOptionsLookup.set(key, value)
|
|
166
|
+
} else if (cliOptionsKeys.includes(key as any)) {
|
|
167
|
+
cliOptionsLookup.set(key, value)
|
|
168
|
+
} else {
|
|
169
|
+
operationsOptionsLookup.set(key, value)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
operationData.operation = operation
|
|
174
|
+
operationData.operationArgs = operationArgs
|
|
175
|
+
|
|
176
|
+
operationData.globalOptions = await optionsLookupToTypedObject(globalOptionsLookup, 'GlobalOptions')
|
|
177
|
+
operationData.cliOptions = await optionsLookupToTypedObject(cliOptionsLookup, 'CLIOptions')
|
|
178
|
+
operationData.operationOptionsLookup = operationsOptionsLookup
|
|
121
179
|
}
|
|
122
180
|
} catch (e: any) {
|
|
123
181
|
resetActiveLogger()
|
|
124
182
|
|
|
125
|
-
logger.logTitledMessage(`Error`, e.message, chalk.redBright)
|
|
183
|
+
logger.logTitledMessage(`Error`, e.message, chalk.redBright, 'error')
|
|
126
184
|
process.exit(1)
|
|
127
185
|
}
|
|
128
186
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
args.options.delete('debug')
|
|
187
|
+
for (const key in operationData.globalOptions) {
|
|
188
|
+
const value = (operationData.globalOptions as any)[key]
|
|
132
189
|
|
|
133
|
-
|
|
190
|
+
API.setGlobalOption(key as any, value)
|
|
134
191
|
}
|
|
135
192
|
|
|
193
|
+
const debugMode = operationData.cliOptions.debug || false
|
|
194
|
+
|
|
136
195
|
try {
|
|
137
|
-
await startWithArgs(
|
|
196
|
+
await startWithArgs(operationData)
|
|
138
197
|
} catch (e: any) {
|
|
139
198
|
resetActiveLogger()
|
|
140
199
|
|
|
141
200
|
if (debugMode) {
|
|
142
|
-
logger.log(e)
|
|
201
|
+
logger.log(e, 'error')
|
|
143
202
|
} else {
|
|
144
|
-
logger.logTitledMessage(`Error`, e.message, chalk.redBright)
|
|
203
|
+
logger.logTitledMessage(`Error`, e.message, chalk.redBright, 'error')
|
|
145
204
|
}
|
|
146
205
|
|
|
147
206
|
process.exit(1)
|
|
@@ -152,7 +211,7 @@ export async function start(processArgs: string[]) {
|
|
|
152
211
|
|
|
153
212
|
const executableName = `${chalk.cyanBright('echogarden')}`
|
|
154
213
|
|
|
155
|
-
const
|
|
214
|
+
const help = [
|
|
156
215
|
`${executableName} ${chalk.magentaBright('speak')} text [output files...] [options...]`,
|
|
157
216
|
` Speak the given text\n`,
|
|
158
217
|
`${executableName} ${chalk.magentaBright('speak-file')} inputFile [output files...] [options...]`,
|
|
@@ -192,124 +251,123 @@ const commandHelp = [
|
|
|
192
251
|
`${executableName} ${chalk.magentaBright('serve')} [options...]`,
|
|
193
252
|
` Start a server\n`,
|
|
194
253
|
`Options reference: ${chalk.blueBright('https://bit.ly/echogarden-options')}`
|
|
195
|
-
//` ${chalk.blueBright('https://github.com/echogarden-project/echogarden/blob/main/docs/Options.md')}`
|
|
196
254
|
]
|
|
197
255
|
|
|
198
|
-
async function startWithArgs(
|
|
256
|
+
async function startWithArgs(operationData: CLIOperationData) {
|
|
199
257
|
const logger = new Logger()
|
|
200
258
|
|
|
201
|
-
switch (
|
|
259
|
+
switch (operationData.operation) {
|
|
202
260
|
case 'speak':
|
|
203
261
|
case 'speak-file':
|
|
204
262
|
case 'speak-url':
|
|
205
263
|
case 'speak-wikipedia': {
|
|
206
|
-
await speak(
|
|
264
|
+
await speak(operationData)
|
|
207
265
|
break
|
|
208
266
|
}
|
|
209
267
|
|
|
210
268
|
case 'transcribe': {
|
|
211
|
-
await transcribe(
|
|
269
|
+
await transcribe(operationData)
|
|
212
270
|
break
|
|
213
271
|
}
|
|
214
272
|
|
|
215
273
|
case 'align': {
|
|
216
|
-
await align(
|
|
274
|
+
await align(operationData)
|
|
217
275
|
break
|
|
218
276
|
}
|
|
219
277
|
|
|
220
278
|
case 'translate-speech': {
|
|
221
|
-
await translateSpeech(
|
|
279
|
+
await translateSpeech(operationData)
|
|
222
280
|
break
|
|
223
281
|
}
|
|
224
282
|
|
|
225
283
|
case 'align-translation': {
|
|
226
|
-
await alignTranslation(
|
|
284
|
+
await alignTranslation(operationData)
|
|
227
285
|
break
|
|
228
286
|
}
|
|
229
287
|
|
|
230
288
|
case 'detect-language': {
|
|
231
|
-
await detectLanguage(
|
|
289
|
+
await detectLanguage(operationData, 'auto')
|
|
232
290
|
break
|
|
233
291
|
}
|
|
234
292
|
|
|
235
293
|
case 'detect-speech-language': {
|
|
236
|
-
await detectLanguage(
|
|
294
|
+
await detectLanguage(operationData, 'speech')
|
|
237
295
|
break
|
|
238
296
|
}
|
|
239
297
|
|
|
240
298
|
case 'detect-text-language': {
|
|
241
|
-
await detectLanguage(
|
|
299
|
+
await detectLanguage(operationData, 'text')
|
|
242
300
|
break
|
|
243
301
|
}
|
|
244
302
|
|
|
245
303
|
case 'detect-voice-activity': {
|
|
246
|
-
await detectVoiceActivity(
|
|
304
|
+
await detectVoiceActivity(operationData)
|
|
247
305
|
break
|
|
248
306
|
}
|
|
249
307
|
|
|
250
308
|
case 'denoise': {
|
|
251
|
-
await denoise(
|
|
309
|
+
await denoise(operationData)
|
|
252
310
|
break
|
|
253
311
|
}
|
|
254
312
|
|
|
255
313
|
case 'isolate': {
|
|
256
|
-
await isolate(
|
|
314
|
+
await isolate(operationData)
|
|
257
315
|
break
|
|
258
316
|
}
|
|
259
317
|
|
|
260
318
|
case 'list-engines': {
|
|
261
|
-
await listEngines(
|
|
319
|
+
await listEngines(operationData)
|
|
262
320
|
break
|
|
263
321
|
}
|
|
264
322
|
|
|
265
323
|
case 'list-voices': {
|
|
266
|
-
await listTTSVoices(
|
|
324
|
+
await listTTSVoices(operationData)
|
|
267
325
|
break
|
|
268
326
|
}
|
|
269
327
|
|
|
270
328
|
case 'install': {
|
|
271
|
-
await installPackages(
|
|
329
|
+
await installPackages(operationData)
|
|
272
330
|
break
|
|
273
331
|
}
|
|
274
332
|
|
|
275
333
|
case 'uninstall': {
|
|
276
|
-
await uninstallPackages(
|
|
334
|
+
await uninstallPackages(operationData)
|
|
277
335
|
break
|
|
278
336
|
}
|
|
279
337
|
|
|
280
338
|
case 'list-packages': {
|
|
281
|
-
await listPackages(
|
|
339
|
+
await listPackages(operationData)
|
|
282
340
|
break
|
|
283
341
|
}
|
|
284
342
|
|
|
285
343
|
case 'serve': {
|
|
286
|
-
await serve(
|
|
344
|
+
await serve(operationData)
|
|
287
345
|
break
|
|
288
346
|
}
|
|
289
347
|
|
|
290
348
|
default: {
|
|
291
|
-
logger.logTitledMessage(`Unknown
|
|
349
|
+
logger.logTitledMessage(`Unknown operation`, operationData.operation, chalk.redBright, 'error')
|
|
292
350
|
process.exit(1)
|
|
293
351
|
}
|
|
294
352
|
}
|
|
295
353
|
}
|
|
296
354
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: Map<string, string>) {
|
|
355
|
+
async function speak(operationData: CLIOperationData) {
|
|
300
356
|
const logger = new Logger()
|
|
301
357
|
|
|
302
|
-
const
|
|
303
|
-
|
|
358
|
+
const { operationArgs, operation, operationOptionsLookup, cliOptions } = operationData
|
|
359
|
+
|
|
360
|
+
const mainArg = operationArgs[0]
|
|
361
|
+
const outputFilenames = operationArgs.slice(1)
|
|
304
362
|
|
|
305
363
|
if (mainArg == undefined) {
|
|
306
|
-
if (
|
|
364
|
+
if (operation == 'speak') {
|
|
307
365
|
throw new Error(`'speak' requires an argument containing the text to speak.`)
|
|
308
|
-
} else if (
|
|
366
|
+
} else if (operation == 'speak-file') {
|
|
309
367
|
throw new Error(`'speak-file' requires an argument containing the file to speak.`)
|
|
310
|
-
} else if (
|
|
368
|
+
} else if (operation == 'speak-url') {
|
|
311
369
|
throw new Error(`'speak-url' requires an argument containing the url to speak.`)
|
|
312
|
-
} else if (
|
|
370
|
+
} else if (operation == 'speak-wikipedia') {
|
|
313
371
|
throw new Error(`'speak-wikipedia' requires an argument containing the name of the Wikipedia article to speak.`)
|
|
314
372
|
}
|
|
315
373
|
|
|
@@ -320,13 +378,13 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
320
378
|
additionalOptionsSchema.set('play', { type: 'boolean' })
|
|
321
379
|
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
322
380
|
|
|
323
|
-
if (
|
|
324
|
-
cliOptions.
|
|
381
|
+
if (cliOptions.play == null) {
|
|
382
|
+
cliOptions.play = outputFilenames.length === 0
|
|
325
383
|
}
|
|
326
384
|
|
|
327
|
-
const options
|
|
385
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SynthesisOptions', additionalOptionsSchema)
|
|
328
386
|
|
|
329
|
-
const allowOverwrite = getWithDefault(
|
|
387
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
330
388
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true)
|
|
331
389
|
|
|
332
390
|
let plainText: string | undefined = undefined
|
|
@@ -335,7 +393,7 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
335
393
|
const plainTextParagraphBreaks = options.plainText?.paragraphBreaks || API.defaultSynthesisOptions.plainText!.paragraphBreaks!
|
|
336
394
|
const plainTextWhitespace = options.plainText?.whitespace || API.defaultSynthesisOptions.plainText!.whitespace!
|
|
337
395
|
|
|
338
|
-
if (
|
|
396
|
+
if (operation == 'speak') {
|
|
339
397
|
if (options.ssml) {
|
|
340
398
|
textSegments = [mainArg]
|
|
341
399
|
} else {
|
|
@@ -343,7 +401,7 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
343
401
|
}
|
|
344
402
|
|
|
345
403
|
plainText = mainArg
|
|
346
|
-
} else if (
|
|
404
|
+
} else if (operation == 'speak-file') {
|
|
347
405
|
const sourceFile = mainArg
|
|
348
406
|
|
|
349
407
|
if (!existsSync(sourceFile)) {
|
|
@@ -373,9 +431,9 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
373
431
|
} else {
|
|
374
432
|
throw new Error(`'speak-file' only supports inputs with extensions 'txt', 'html', 'htm', 'xml', 'ssml', 'srt', 'vtt'`)
|
|
375
433
|
}
|
|
376
|
-
} else if (
|
|
434
|
+
} else if (operation == 'speak-url') {
|
|
377
435
|
if (options.ssml) {
|
|
378
|
-
throw new Error(`speak-url doesn't
|
|
436
|
+
throw new Error(`speak-url doesn't accept SSML inputs`)
|
|
379
437
|
}
|
|
380
438
|
|
|
381
439
|
const url = mainArg
|
|
@@ -388,7 +446,7 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
388
446
|
const textContent = await fetchDocumentText(url)
|
|
389
447
|
|
|
390
448
|
textSegments = splitToParagraphs(textContent, 'single', 'preserve')
|
|
391
|
-
} else if (
|
|
449
|
+
} else if (operation == 'speak-wikipedia') {
|
|
392
450
|
if (options.ssml) {
|
|
393
451
|
throw new Error(`speak-wikipedia doesn't provide SSML inputs`)
|
|
394
452
|
}
|
|
@@ -400,7 +458,7 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
400
458
|
|
|
401
459
|
textSegments = await parseWikipediaArticle(mainArg, getShortLanguageCode(options.language))
|
|
402
460
|
} else {
|
|
403
|
-
throw new Error(
|
|
461
|
+
throw new Error(`Invalid operation specified: '${operation}'`)
|
|
404
462
|
}
|
|
405
463
|
|
|
406
464
|
async function onSegment(segmentData: API.SynthesisSegmentEventData) {
|
|
@@ -412,7 +470,7 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
412
470
|
|
|
413
471
|
logger.end()
|
|
414
472
|
|
|
415
|
-
if (
|
|
473
|
+
if (cliOptions.play) {
|
|
416
474
|
let gainAmount = -3 - segmentData.peakDecibelsSoFar
|
|
417
475
|
//gainAmount = Math.min(gainAmount, 0)
|
|
418
476
|
|
|
@@ -451,11 +509,13 @@ async function speak(command: SpeakCommand, commandArgs: string[], cliOptions: M
|
|
|
451
509
|
logger.end()
|
|
452
510
|
}
|
|
453
511
|
|
|
454
|
-
async function transcribe(
|
|
512
|
+
async function transcribe(operationData: CLIOperationData) {
|
|
455
513
|
const logger = new Logger()
|
|
456
514
|
|
|
457
|
-
const
|
|
458
|
-
|
|
515
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
516
|
+
|
|
517
|
+
const sourceFilename = operationArgs[0]
|
|
518
|
+
const outputFilenames = operationArgs.slice(1)
|
|
459
519
|
|
|
460
520
|
if (sourceFilename == undefined) {
|
|
461
521
|
throw new Error(`'transcribe' requires an argument containing the source file name.`)
|
|
@@ -465,17 +525,13 @@ async function transcribe(commandArgs: string[], cliOptions: Map<string, string>
|
|
|
465
525
|
throw new Error(`The given source audio file '${sourceFilename}' was not found.`)
|
|
466
526
|
}
|
|
467
527
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
471
|
-
|
|
472
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
473
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`)
|
|
528
|
+
if (cliOptions.play == null) {
|
|
529
|
+
cliOptions.play = outputFilenames.length === 0
|
|
474
530
|
}
|
|
475
531
|
|
|
476
|
-
const options
|
|
532
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'RecognitionOptions')
|
|
477
533
|
|
|
478
|
-
const allowOverwrite = getWithDefault(
|
|
534
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
479
535
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true)
|
|
480
536
|
|
|
481
537
|
const { transcript, timeline, wordTimeline, language, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.recognize(sourceFilename, options)
|
|
@@ -500,7 +556,7 @@ async function transcribe(commandArgs: string[], cliOptions: Map<string, string>
|
|
|
500
556
|
|
|
501
557
|
logger.end()
|
|
502
558
|
|
|
503
|
-
if (
|
|
559
|
+
if (cliOptions.play) {
|
|
504
560
|
let audioToPlay: RawAudio
|
|
505
561
|
|
|
506
562
|
if (isolatedRawAudio) {
|
|
@@ -515,11 +571,13 @@ async function transcribe(commandArgs: string[], cliOptions: Map<string, string>
|
|
|
515
571
|
}
|
|
516
572
|
}
|
|
517
573
|
|
|
518
|
-
async function align(
|
|
574
|
+
async function align(operationData: CLIOperationData) {
|
|
519
575
|
const logger = new Logger()
|
|
520
576
|
|
|
521
|
-
const
|
|
522
|
-
|
|
577
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
578
|
+
|
|
579
|
+
const audioFilename = operationArgs[0]
|
|
580
|
+
const outputFilenames = operationArgs.slice(2)
|
|
523
581
|
|
|
524
582
|
if (audioFilename == undefined) {
|
|
525
583
|
throw new Error(`align requires an argument containing the audio file path.`)
|
|
@@ -529,7 +587,7 @@ async function align(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
529
587
|
throw new Error(`The given source file '${audioFilename}' was not found.`)
|
|
530
588
|
}
|
|
531
589
|
|
|
532
|
-
const alignmentReferenceFile =
|
|
590
|
+
const alignmentReferenceFile = operationArgs[1]
|
|
533
591
|
|
|
534
592
|
if (alignmentReferenceFile == undefined) {
|
|
535
593
|
throw new Error(`align requires a second argument containing the alignment reference file path.`)
|
|
@@ -554,17 +612,12 @@ async function align(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
554
612
|
throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`)
|
|
555
613
|
}
|
|
556
614
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
560
|
-
|
|
561
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
562
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`)
|
|
615
|
+
if (cliOptions.play == null) {
|
|
616
|
+
cliOptions.play = outputFilenames.length === 0
|
|
563
617
|
}
|
|
618
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'AlignmentOptions')
|
|
564
619
|
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
const allowOverwrite = getWithDefault((options as any).overwrite, overwriteByDefault)
|
|
620
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
568
621
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true)
|
|
569
622
|
|
|
570
623
|
const { timeline, wordTimeline, transcript, language, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.align(audioFilename, text, options)
|
|
@@ -599,7 +652,7 @@ async function align(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
599
652
|
|
|
600
653
|
logger.end()
|
|
601
654
|
|
|
602
|
-
if (
|
|
655
|
+
if (cliOptions.play) {
|
|
603
656
|
let audioToPlay: RawAudio
|
|
604
657
|
|
|
605
658
|
if (isolatedRawAudio) {
|
|
@@ -614,11 +667,13 @@ async function align(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
614
667
|
}
|
|
615
668
|
}
|
|
616
669
|
|
|
617
|
-
async function alignTranslation(
|
|
670
|
+
async function alignTranslation(operationData: CLIOperationData) {
|
|
618
671
|
const logger = new Logger()
|
|
619
672
|
|
|
620
|
-
const
|
|
621
|
-
|
|
673
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
674
|
+
|
|
675
|
+
const audioFilename = operationArgs[0]
|
|
676
|
+
const outputFilenames = operationArgs.slice(2)
|
|
622
677
|
|
|
623
678
|
if (audioFilename == undefined) {
|
|
624
679
|
throw new Error(`align-translation requires an argument containing the audio file path.`)
|
|
@@ -628,7 +683,7 @@ async function alignTranslation(commandArgs: string[], cliOptions: Map<string, s
|
|
|
628
683
|
throw new Error(`The given source file '${audioFilename}' was not found.`)
|
|
629
684
|
}
|
|
630
685
|
|
|
631
|
-
const alignmentReferenceFile =
|
|
686
|
+
const alignmentReferenceFile = operationArgs[1]
|
|
632
687
|
|
|
633
688
|
if (alignmentReferenceFile == undefined) {
|
|
634
689
|
throw new Error(`align-translation requires a second argument containing the translated reference file path.`)
|
|
@@ -653,17 +708,13 @@ async function alignTranslation(commandArgs: string[], cliOptions: Map<string, s
|
|
|
653
708
|
throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`)
|
|
654
709
|
}
|
|
655
710
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
659
|
-
|
|
660
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
661
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`)
|
|
711
|
+
if (cliOptions.play == null) {
|
|
712
|
+
cliOptions.play = outputFilenames.length === 0
|
|
662
713
|
}
|
|
663
714
|
|
|
664
|
-
const options
|
|
715
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TranslationAlignmentOptions')
|
|
665
716
|
|
|
666
|
-
const allowOverwrite = getWithDefault(
|
|
717
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
667
718
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true)
|
|
668
719
|
|
|
669
720
|
const {
|
|
@@ -705,7 +756,7 @@ async function alignTranslation(commandArgs: string[], cliOptions: Map<string, s
|
|
|
705
756
|
|
|
706
757
|
logger.end()
|
|
707
758
|
|
|
708
|
-
if (
|
|
759
|
+
if (cliOptions.play) {
|
|
709
760
|
let audioToPlay: RawAudio
|
|
710
761
|
|
|
711
762
|
if (isolatedRawAudio) {
|
|
@@ -720,11 +771,13 @@ async function alignTranslation(commandArgs: string[], cliOptions: Map<string, s
|
|
|
720
771
|
}
|
|
721
772
|
}
|
|
722
773
|
|
|
723
|
-
async function translateSpeech(
|
|
774
|
+
async function translateSpeech(operationData: CLIOperationData) {
|
|
724
775
|
const logger = new Logger()
|
|
725
776
|
|
|
726
|
-
const
|
|
727
|
-
|
|
777
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
778
|
+
|
|
779
|
+
const inputFilename = operationArgs[0]
|
|
780
|
+
const outputFilenames = operationArgs.slice(1)
|
|
728
781
|
|
|
729
782
|
if (inputFilename == undefined) {
|
|
730
783
|
throw new Error(`translate-speech requires an argument containing the input file path.`)
|
|
@@ -734,17 +787,13 @@ async function translateSpeech(commandArgs: string[], cliOptions: Map<string, st
|
|
|
734
787
|
throw new Error(`The given input file '${inputFilename}' was not found.`)
|
|
735
788
|
}
|
|
736
789
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
740
|
-
|
|
741
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
742
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`)
|
|
790
|
+
if (cliOptions.play == null) {
|
|
791
|
+
cliOptions.play = outputFilenames.length === 0
|
|
743
792
|
}
|
|
744
793
|
|
|
745
|
-
const options
|
|
794
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SpeechTranslationOptions')
|
|
746
795
|
|
|
747
|
-
const allowOverwrite = getWithDefault(
|
|
796
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
748
797
|
|
|
749
798
|
await checkOutputFilenames(outputFilenames, true, true, true)
|
|
750
799
|
|
|
@@ -770,7 +819,7 @@ async function translateSpeech(commandArgs: string[], cliOptions: Map<string, st
|
|
|
770
819
|
|
|
771
820
|
logger.end()
|
|
772
821
|
|
|
773
|
-
if (
|
|
822
|
+
if (cliOptions.play) {
|
|
774
823
|
let audioToPlay: RawAudio
|
|
775
824
|
|
|
776
825
|
if (isolatedRawAudio) {
|
|
@@ -809,19 +858,18 @@ async function translateSpeech(commandArgs: string[], cliOptions: Map<string, st
|
|
|
809
858
|
}
|
|
810
859
|
}
|
|
811
860
|
|
|
812
|
-
async function detectLanguage(
|
|
861
|
+
async function detectLanguage(operationData: CLIOperationData, mode: 'speech' | 'text' | 'auto') {
|
|
813
862
|
const logger = new Logger()
|
|
814
863
|
|
|
815
|
-
const
|
|
816
|
-
|
|
864
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
865
|
+
|
|
866
|
+
const inputFilePath = operationArgs[0]
|
|
867
|
+
const outputFilenames = operationArgs.slice(1)
|
|
817
868
|
|
|
818
869
|
if (!existsSync(inputFilePath)) {
|
|
819
870
|
throw new Error(`The given input file '${inputFilePath}' was not found.`)
|
|
820
871
|
}
|
|
821
872
|
|
|
822
|
-
const additionalOptionsSchema = new Map<string, SchemaTypeDefinition>()
|
|
823
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
824
|
-
|
|
825
873
|
const inputFileExtension = getLowercaseFileExtension(inputFilePath)
|
|
826
874
|
const supportedInputTextFormats = ['txt', 'srt', 'vtt']
|
|
827
875
|
|
|
@@ -838,8 +886,8 @@ async function detectLanguage(commandArgs: string[], cliOptions: Map<string, str
|
|
|
838
886
|
throw new Error(`'detect-text-language' doesn't support input file extension '${inputFileExtension}'`)
|
|
839
887
|
}
|
|
840
888
|
|
|
841
|
-
const options
|
|
842
|
-
allowOverwrite = getWithDefault(
|
|
889
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TextLanguageDetectionOptions')
|
|
890
|
+
allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
843
891
|
|
|
844
892
|
await checkOutputFilenames(outputFilenames, false, true, false)
|
|
845
893
|
|
|
@@ -857,8 +905,8 @@ async function detectLanguage(commandArgs: string[], cliOptions: Map<string, str
|
|
|
857
905
|
throw new Error(`detect-speech-language requires an argument containing the input audio file path.`)
|
|
858
906
|
}
|
|
859
907
|
|
|
860
|
-
const options
|
|
861
|
-
allowOverwrite = getWithDefault(
|
|
908
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SpeechLanguageDetectionOptions')
|
|
909
|
+
allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
862
910
|
|
|
863
911
|
await checkOutputFilenames(outputFilenames, false, true, false)
|
|
864
912
|
|
|
@@ -880,18 +928,20 @@ async function detectLanguage(commandArgs: string[], cliOptions: Map<string, str
|
|
|
880
928
|
} else {
|
|
881
929
|
const resultsAsText = results.slice(0, 10).map(result => `${formatLanguageCodeWithName(result.language)}: ${result.probability.toFixed(5)}`).join('\n')
|
|
882
930
|
|
|
883
|
-
logger.log('')
|
|
884
|
-
logger.log(resultsAsText)
|
|
931
|
+
logger.log('', 'output')
|
|
932
|
+
logger.log(resultsAsText, 'output')
|
|
885
933
|
}
|
|
886
934
|
|
|
887
935
|
logger.end()
|
|
888
936
|
}
|
|
889
937
|
|
|
890
|
-
async function detectVoiceActivity(
|
|
938
|
+
async function detectVoiceActivity(operationData: CLIOperationData) {
|
|
891
939
|
const logger = new Logger()
|
|
892
940
|
|
|
893
|
-
const
|
|
894
|
-
|
|
941
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
942
|
+
|
|
943
|
+
const audioFilename = operationArgs[0]
|
|
944
|
+
const outputFilenames = operationArgs.slice(1)
|
|
895
945
|
|
|
896
946
|
if (audioFilename == undefined) {
|
|
897
947
|
throw new Error(`detect-voice-activity requires an argument containing the audio file path.`)
|
|
@@ -901,17 +951,13 @@ async function detectVoiceActivity(commandArgs: string[], cliOptions: Map<string
|
|
|
901
951
|
throw new Error(`The given source audio file '${audioFilename}' was not found.`)
|
|
902
952
|
}
|
|
903
953
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
907
|
-
|
|
908
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
909
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`)
|
|
954
|
+
if (cliOptions.play == null) {
|
|
955
|
+
cliOptions.play = outputFilenames.length === 0
|
|
910
956
|
}
|
|
911
957
|
|
|
912
|
-
const options
|
|
958
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'VADOptions')
|
|
913
959
|
|
|
914
|
-
const allowOverwrite = getWithDefault(
|
|
960
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
915
961
|
|
|
916
962
|
await checkOutputFilenames(outputFilenames, true, true, true)
|
|
917
963
|
|
|
@@ -947,7 +993,7 @@ async function detectVoiceActivity(commandArgs: string[], cliOptions: Map<string
|
|
|
947
993
|
|
|
948
994
|
logger.end()
|
|
949
995
|
|
|
950
|
-
if (
|
|
996
|
+
if (cliOptions.play) {
|
|
951
997
|
const normalizedAudio = normalizeAudioLevel(inputRawAudio)
|
|
952
998
|
|
|
953
999
|
const timelineToPlay = verboseTimeline.map(entry => {
|
|
@@ -958,11 +1004,13 @@ async function detectVoiceActivity(commandArgs: string[], cliOptions: Map<string
|
|
|
958
1004
|
}
|
|
959
1005
|
}
|
|
960
1006
|
|
|
961
|
-
async function denoise(
|
|
1007
|
+
async function denoise(operationData: CLIOperationData) {
|
|
962
1008
|
const logger = new Logger()
|
|
963
1009
|
|
|
964
|
-
const
|
|
965
|
-
|
|
1010
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
1011
|
+
|
|
1012
|
+
const audioFilename = operationArgs[0]
|
|
1013
|
+
const outputFilenames = operationArgs.slice(1)
|
|
966
1014
|
|
|
967
1015
|
if (audioFilename == undefined) {
|
|
968
1016
|
throw new Error(`'denoise' requires an argument containing the audio file path.`)
|
|
@@ -972,17 +1020,13 @@ async function denoise(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
972
1020
|
throw new Error(`The given source audio file '${audioFilename}' was not found.`)
|
|
973
1021
|
}
|
|
974
1022
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
978
|
-
|
|
979
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
980
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`)
|
|
1023
|
+
if (cliOptions.play == null) {
|
|
1024
|
+
cliOptions.play = outputFilenames.length === 0
|
|
981
1025
|
}
|
|
982
1026
|
|
|
983
|
-
const options
|
|
1027
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'DenoisingOptions')
|
|
984
1028
|
|
|
985
|
-
const allowOverwrite = getWithDefault(
|
|
1029
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
986
1030
|
|
|
987
1031
|
await checkOutputFilenames(outputFilenames, true, false, false)
|
|
988
1032
|
|
|
@@ -1000,16 +1044,18 @@ async function denoise(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
1000
1044
|
|
|
1001
1045
|
logger.end()
|
|
1002
1046
|
|
|
1003
|
-
if (
|
|
1047
|
+
if (cliOptions.play) {
|
|
1004
1048
|
await playAudioSamples(denoisedAudio)
|
|
1005
1049
|
}
|
|
1006
1050
|
}
|
|
1007
1051
|
|
|
1008
|
-
async function isolate(
|
|
1052
|
+
async function isolate(operationData: CLIOperationData) {
|
|
1009
1053
|
const logger = new Logger()
|
|
1010
1054
|
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1055
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
1056
|
+
|
|
1057
|
+
const audioFilename = operationArgs[0]
|
|
1058
|
+
const outputFilenames = operationArgs.slice(1)
|
|
1013
1059
|
|
|
1014
1060
|
if (audioFilename == undefined) {
|
|
1015
1061
|
throw new Error(`'isolate' requires an argument containing the audio file path.`)
|
|
@@ -1019,17 +1065,13 @@ async function isolate(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
1019
1065
|
throw new Error(`The given source audio file '${audioFilename}' was not found.`)
|
|
1020
1066
|
}
|
|
1021
1067
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
1025
|
-
|
|
1026
|
-
if (!cliOptions.has('play') && !cliOptions.has('no-play')) {
|
|
1027
|
-
cliOptions.set('play', `${outputFilenames.length == 0}`)
|
|
1068
|
+
if (cliOptions.play == null) {
|
|
1069
|
+
cliOptions.play = outputFilenames.length === 0
|
|
1028
1070
|
}
|
|
1029
1071
|
|
|
1030
|
-
const options
|
|
1072
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'SourceSeparationOptions')
|
|
1031
1073
|
|
|
1032
|
-
const allowOverwrite = getWithDefault(
|
|
1074
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
1033
1075
|
|
|
1034
1076
|
await checkOutputFilenames(outputFilenames, true, false, false)
|
|
1035
1077
|
|
|
@@ -1045,18 +1087,20 @@ async function isolate(commandArgs: string[], cliOptions: Map<string, string>) {
|
|
|
1045
1087
|
|
|
1046
1088
|
logger.end()
|
|
1047
1089
|
|
|
1048
|
-
if (
|
|
1090
|
+
if (cliOptions.play) {
|
|
1049
1091
|
await playAudioSamples(isolatedRawAudio)
|
|
1050
1092
|
}
|
|
1051
1093
|
}
|
|
1052
1094
|
|
|
1053
|
-
async function listEngines(
|
|
1095
|
+
async function listEngines(operationData: CLIOperationData) {
|
|
1054
1096
|
const logger = new Logger()
|
|
1055
1097
|
|
|
1056
|
-
const
|
|
1098
|
+
const { operationArgs } = operationData
|
|
1099
|
+
|
|
1100
|
+
const targetOperation = operationArgs[0]
|
|
1057
1101
|
|
|
1058
1102
|
if (!targetOperation) {
|
|
1059
|
-
throw new Error(`The 'list-engines'
|
|
1103
|
+
throw new Error(`The 'list-engines' operation requires an argument specifying the operation to list engines for, like 'echogarden list-engines transcribe'.`)
|
|
1060
1104
|
}
|
|
1061
1105
|
|
|
1062
1106
|
let engines: API.EngineMetadata[]
|
|
@@ -1150,22 +1194,24 @@ async function listEngines(commandArgs: string[], cliOptions: Map<string, string
|
|
|
1150
1194
|
}
|
|
1151
1195
|
|
|
1152
1196
|
for (const [index, engine] of engines.entries()) {
|
|
1153
|
-
logger.logTitledMessage('Identifier', chalk.magentaBright(engine.id))
|
|
1154
|
-
logger.logTitledMessage('Name', engine.name)
|
|
1155
|
-
logger.logTitledMessage('Description', engine.description)
|
|
1156
|
-
logger.logTitledMessage('Type', engine.type)
|
|
1197
|
+
logger.logTitledMessage('Identifier', chalk.magentaBright(engine.id), undefined, 'output')
|
|
1198
|
+
logger.logTitledMessage('Name', engine.name, undefined, 'output')
|
|
1199
|
+
logger.logTitledMessage('Description', engine.description, undefined, 'output')
|
|
1200
|
+
logger.logTitledMessage('Type', engine.type, undefined, 'output')
|
|
1157
1201
|
|
|
1158
1202
|
if (index < engines.length - 1) {
|
|
1159
|
-
logger.log('')
|
|
1203
|
+
logger.log('', 'output')
|
|
1160
1204
|
}
|
|
1161
1205
|
}
|
|
1162
1206
|
}
|
|
1163
1207
|
|
|
1164
|
-
async function listTTSVoices(
|
|
1208
|
+
async function listTTSVoices(operationData: CLIOperationData) {
|
|
1165
1209
|
const logger = new Logger()
|
|
1166
1210
|
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1211
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData
|
|
1212
|
+
|
|
1213
|
+
const targetEngine = operationArgs[0]
|
|
1214
|
+
const outputFilenames = operationArgs.slice(1)
|
|
1169
1215
|
|
|
1170
1216
|
if (!targetEngine) {
|
|
1171
1217
|
const optionsSchema = await getOptionsSchema()
|
|
@@ -1177,11 +1223,11 @@ async function listTTSVoices(commandArgs: string[], cliOptions: Map<string, stri
|
|
|
1177
1223
|
const additionalOptionsSchema = new Map<string, SchemaTypeDefinition>()
|
|
1178
1224
|
additionalOptionsSchema.set('overwrite', { type: 'boolean' })
|
|
1179
1225
|
|
|
1180
|
-
|
|
1226
|
+
operationOptionsLookup.set('engine', targetEngine)
|
|
1181
1227
|
|
|
1182
|
-
const options
|
|
1228
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'VoiceListRequestOptions')
|
|
1183
1229
|
|
|
1184
|
-
const allowOverwrite = getWithDefault(
|
|
1230
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault)
|
|
1185
1231
|
|
|
1186
1232
|
await checkOutputFilenames(outputFilenames, false, true, false)
|
|
1187
1233
|
|
|
@@ -1215,67 +1261,71 @@ async function listTTSVoices(commandArgs: string[], cliOptions: Map<string, stri
|
|
|
1215
1261
|
await fileSaver(getEmptyRawAudio(0, 0), voiceList as any, voiceListTextWithoutColors)
|
|
1216
1262
|
}
|
|
1217
1263
|
} else {
|
|
1218
|
-
logger.log(voiceListText)
|
|
1264
|
+
logger.log(voiceListText, 'output')
|
|
1219
1265
|
}
|
|
1220
1266
|
|
|
1221
1267
|
logger.end()
|
|
1222
1268
|
}
|
|
1223
1269
|
|
|
1224
|
-
async function installPackages(
|
|
1270
|
+
async function installPackages(operationData: CLIOperationData) {
|
|
1225
1271
|
const logger = new Logger()
|
|
1226
1272
|
|
|
1227
|
-
|
|
1273
|
+
const { operationArgs } = operationData
|
|
1274
|
+
|
|
1275
|
+
if (operationArgs.length == 0) {
|
|
1228
1276
|
throw new Error('No package names specified')
|
|
1229
1277
|
}
|
|
1230
1278
|
|
|
1231
1279
|
const failedPackageNames: string[] = []
|
|
1232
1280
|
|
|
1233
|
-
for (const packageName of
|
|
1281
|
+
for (const packageName of operationArgs) {
|
|
1234
1282
|
try {
|
|
1235
1283
|
await loadPackage(packageName)
|
|
1236
1284
|
} catch (e) {
|
|
1237
1285
|
resetActiveLogger()
|
|
1238
1286
|
|
|
1239
|
-
logger.
|
|
1287
|
+
logger.logTitledMessage(`Failed installing package ${packageName}`, e, chalk.redBright, 'error')
|
|
1240
1288
|
failedPackageNames.push(packageName)
|
|
1241
1289
|
}
|
|
1242
1290
|
}
|
|
1243
1291
|
|
|
1244
1292
|
if (failedPackageNames.length > 0) {
|
|
1245
1293
|
if (failedPackageNames.length == 1) {
|
|
1246
|
-
logger.log(`The package ${failedPackageNames[0]} failed to install
|
|
1294
|
+
logger.log(`The package ${failedPackageNames[0]} failed to install`, 'error')
|
|
1247
1295
|
} else {
|
|
1248
|
-
logger.log(`The packages ${failedPackageNames.join(', ')} failed to install
|
|
1296
|
+
logger.log(`The packages ${failedPackageNames.join(', ')} failed to install`, 'error')
|
|
1249
1297
|
}
|
|
1250
1298
|
}
|
|
1251
1299
|
}
|
|
1252
1300
|
|
|
1253
|
-
async function uninstallPackages(
|
|
1301
|
+
async function uninstallPackages(operationData: CLIOperationData) {
|
|
1254
1302
|
const logger = new Logger()
|
|
1255
1303
|
|
|
1256
|
-
|
|
1304
|
+
const { operationArgs } = operationData
|
|
1305
|
+
|
|
1306
|
+
if (operationArgs.length == 0) {
|
|
1257
1307
|
throw new Error('No package names specified')
|
|
1258
1308
|
}
|
|
1259
1309
|
|
|
1260
1310
|
const failedPackageNames: string[] = []
|
|
1261
1311
|
|
|
1262
|
-
for (const packageName of
|
|
1312
|
+
for (const packageName of operationArgs) {
|
|
1263
1313
|
try {
|
|
1264
1314
|
await removePackage(packageName)
|
|
1265
1315
|
} catch (e) {
|
|
1266
1316
|
resetActiveLogger()
|
|
1267
1317
|
|
|
1268
|
-
logger.
|
|
1318
|
+
logger.logTitledMessage(`Failed uninstalling package ${packageName}`, e, chalk.redBright, 'error')
|
|
1269
1319
|
failedPackageNames.push(packageName)
|
|
1270
1320
|
}
|
|
1271
1321
|
}
|
|
1272
1322
|
|
|
1273
1323
|
if (failedPackageNames.length > 0) {
|
|
1274
|
-
logger.log(`The packages ${failedPackageNames.join(', ')} failed to uninstall
|
|
1324
|
+
logger.log(`The packages ${failedPackageNames.join(', ')} failed to uninstall`, 'error')
|
|
1275
1325
|
}
|
|
1276
1326
|
}
|
|
1277
1327
|
|
|
1278
|
-
async function listPackages(
|
|
1328
|
+
async function listPackages(operationData: CLIOperationData) {
|
|
1279
1329
|
const logger = new Logger()
|
|
1280
1330
|
|
|
1281
1331
|
const packagesDir = await ensureAndGetPackagesDir()
|
|
@@ -1302,13 +1352,15 @@ async function listPackages(commandArgs: string[], cliOptions: Map<string, strin
|
|
|
1302
1352
|
|
|
1303
1353
|
installedPackageNamesFormatted.sort()
|
|
1304
1354
|
|
|
1305
|
-
logger.log(`Total of ${installedPackageNamesFormatted.length} packages installed in '${packagesDir}'
|
|
1306
|
-
|
|
1355
|
+
logger.log(`Total of ${installedPackageNamesFormatted.length} packages installed in '${packagesDir}'`)
|
|
1356
|
+
logger.log(``)
|
|
1307
1357
|
logger.log(installedPackageNamesFormatted.join('\n'))
|
|
1308
1358
|
}
|
|
1309
1359
|
|
|
1310
|
-
async function serve(
|
|
1311
|
-
const
|
|
1360
|
+
async function serve(operationData: CLIOperationData) {
|
|
1361
|
+
const { operationOptionsLookup } = operationData
|
|
1362
|
+
|
|
1363
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'ServerOptions')
|
|
1312
1364
|
|
|
1313
1365
|
async function onServerStarted(serverOptions: ServerOptions) {
|
|
1314
1366
|
// Run a test routine (early development)
|
|
@@ -1343,7 +1395,7 @@ async function writeSourceSeparationOutputIfNeeded(outputFilename: string, isola
|
|
|
1343
1395
|
}
|
|
1344
1396
|
}
|
|
1345
1397
|
|
|
1346
|
-
async function
|
|
1398
|
+
async function optionsLookupToTypedObject<K extends keyof APIOptions>(cliOptionsMap: Map<string, string>, optionsRoot: K, additionalOptionsSchema?: Map<string, SchemaTypeDefinition>): Promise<APIOptions[K]> {
|
|
1347
1399
|
const optionsSchema = await getOptionsSchema()
|
|
1348
1400
|
const resultingObj: any = {}
|
|
1349
1401
|
|