list_elevenlabs_voices
Version:
List eleven labs voices in the console. Output as text or json.
179 lines (119 loc) • 6.1 kB
text/typescript
// program
import { readFileSync, writeFileSync } from 'fs';
import { base, item, name, misc, is_color } from './color';
const pack = readFileSync('./package.json').toString(),
pkg = JSON.parse(pack.trim());
const { Command } = require('commander');
const program = new Command();
program
.name(pkg.name)
.description(pkg.description)
.version(pkg.version);
program
.option('-p, --private', 'only list your own private voices; skip premades')
.option('-k, --api-key <key>', 'specify an api key to get private voices')
.option('-e, --key-envvar <e>', 'specify an envvar name to get an api key from')
.option('-q, --quiet', 'do not consolize voices (not the same as silent)')
.option('-s, --silent', 'no output at all (todo)')
.option('-c, --color', 'colorize output (default; not for json)')
.option('-b, --basic', 'basic output (no color, not json)')
.option('-j, --as-json', 'report as json, instead of ascii tabled')
.option('-f, --as-formatted-json', 'report as formatted multiline json')
.option('-o, --output-json <fname>', 'store json to disk, irrespective of display');
program.parse(process.argv);
const options = program.opts(),
api_key = options.apiKey ?? process.env[options.keyEnvvar] ?? process.env['LIST_ELEVENLABS_VOICES_APIKEY'];
if (typeof api_key !== 'string') { throw new TypeError('No valid API key or envvar'); }
if (options.basic !== undefined) { is_color(false) };
// needs an api key or a key envvar, can't skip
if (api_key === undefined) {
throw new SyntaxError(base( `Specify either an ${item('API key')} or a ${item('Key Envvar')}, or set envvar ${item('LIST_ELEVENLABS_VOICES_APIKEY')}`));
}
// can't have both
if (options.apiKey && options.keyEnvvar) {
throw new SyntaxError(base( `Specify either an ${item('API key')} or a ${item('Key Envvar')}; not both`));
}
// no two values in this object may be specified as arguments. keys are
// how commander rewrites them for caps, gag.
const no_pair_of: any = {'color' : 'color',
'asJson' : 'as-json',
'asFormattedJson' : 'as-formatted-json',
'quiet' : 'quiet',
'silent' : 'silent'},
npo_keys = Object.keys(no_pair_of);
for (let j=0, jC=npo_keys.length; j<jC; ++j) {
for (let i=j+1, iC=npo_keys.length; i<iC; ++i) {
if (options[npo_keys[i] ?? ''] && options[npo_keys[j] ?? '']) {
throw new SyntaxError(base( `Specify either ${item(no_pair_of[npo_keys[i] ?? ''] ?? '###1')} or ${item(no_pair_of[npo_keys[j] ?? ''] ?? '###2')}, not both`));
}
}
}
// // color and as-json are mutually exclusive
// if (options.color && options.asJson) {
// throw new SyntaxError(base( `Specify either ${item('color')} or ${item('as-json')}`));
// }
// // so are color and as-formatted-json
// if (options.color && options.asFormattedJson) {
// throw new SyntaxError(base( `Specify either ${item('color')} or ${item('as-formatted-json')}`));
// }
// // and so are as-json and as-formatted-json
// if (options.asJson && options.asFormattedJson) {
// throw new SyntaxError(base( `Specify either ${item('as-json')} or ${item('as-formatted-json')}`));
// }
// // ... so are as-json and no-output
// if (options.asJson && options.asFormattedJson) {
// throw new SyntaxError(base( `Specify either ${item('as-json')} or ${item('as-formatted-json')}`));
// }
// // ... and so are as-formatted-json and no-output
// if (options.asJson && options.asFormattedJson) {
// throw new SyntaxError(base( `Specify either ${item('as-json')} or ${item('as-formatted-json')}`));
// }
// // aaaaaaand so are color and no-p
// if (options.asJson && options.asFormattedJson) {
// throw new SyntaxError(base( `Specify either ${item('as-json')} or ${item('as-formatted-json')}`));
// }
async function attempt_backend_call() {
const url = 'https://api.elevenlabs.io/v1/voices',
headers = { 'Content-Type' : 'application/json',
'xi-api-key' : api_key };
const response = await fetch(url, { method: 'GET', headers }),
json = await response.json();
return json;
}
function emit_basic_output(voices: any) {
// right now they're the same, and controlled by the lead-in flag
// left as a wrapper in case that changes
emit_color_output(voices);
}
function emit_color_output(voices: any) {
voices.map( (v: any) => {
const name_col = 30;
const name_sub = (v.name.length < name_col? v.name : (v.name.substring(0, name_col-3) + '...')).padEnd(name_col);
console.log(base( `${misc(v.category.padStart(10))} ${item(v.voice_id)} ${name(name_sub)} ` ));
});
}
function emit_json_output(voices: any) {
console.log( JSON.stringify(voices) );
}
function emit_formatted_json_output(voices: any) {
console.log( JSON.stringify(voices, undefined, 2) );
}
async function handle_cli() {
const o_voices = await attempt_backend_call();
const voices = options.private
? o_voices.voices.filter( (v: any) => v.category !== 'premade')
: o_voices.voices;
if (options.outputJson && (typeof options.outputJson === 'string')) {
if (!(options.silent)) {
console.log('writing to ' + options.outputJson);
}
writeFileSync(options.outputJson, JSON.stringify(voices));
}
if (options.color) { emit_color_output(voices); }
else if (options.asJson) { emit_json_output(voices); }
else if (options.asFormattedJson) { emit_formatted_json_output(voices); }
else if (options.quiet) { /* don't emit anything here */ }
else if (options.silent) { /* don't emit anything here */ }
else { emit_basic_output(voices); }
}
handle_cli();