n8n-nodes-tts-bigboss 1.0.2 → 1.0.4
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/dist/TTSBigBoss.node.js +92 -25
- package/nodes/TTSBigBoss/TTSBigBoss.node.ts +122 -33
- package/package.json +1 -1
package/dist/TTSBigBoss.node.js
CHANGED
|
@@ -47,6 +47,30 @@ const https = __importStar(require("https"));
|
|
|
47
47
|
const stream = __importStar(require("stream"));
|
|
48
48
|
const util_1 = require("util");
|
|
49
49
|
const pipeline = (0, util_1.promisify)(stream.pipeline);
|
|
50
|
+
const PIPER_MODELS = [
|
|
51
|
+
{ name: 'Arabic (Jordan) - Kareem (Male) - Low', value: 'ar_JO-kareem-low' },
|
|
52
|
+
{ name: 'Arabic (Jordan) - Kareem (Male) - Medium', value: 'ar_JO-kareem-medium' },
|
|
53
|
+
{ name: 'English (US) - Lessac (Female) - Low', value: 'en_US-lessac-low' },
|
|
54
|
+
{ name: 'English (US) - Lessac (Female) - Medium', value: 'en_US-lessac-medium' },
|
|
55
|
+
{ name: 'English (US) - Lessac (Female) - High', value: 'en_US-lessac-high' },
|
|
56
|
+
{ name: 'English (US) - Ryan (Male) - Low', value: 'en_US-ryan-low' },
|
|
57
|
+
{ name: 'English (US) - Ryan (Male) - Medium', value: 'en_US-ryan-medium' },
|
|
58
|
+
{ name: 'English (US) - Ryan (Male) - High', value: 'en_US-ryan-high' },
|
|
59
|
+
{ name: 'English (US) - Amy (Female) - Low', value: 'en_US-amy-low' },
|
|
60
|
+
{ name: 'English (US) - Amy (Female) - Medium', value: 'en_US-amy-medium' },
|
|
61
|
+
{ name: 'English (US) - Kathleen (Female) - Low', value: 'en_US-kathleen-low' },
|
|
62
|
+
{ name: 'English (UK) - Alan (Male) - Low', value: 'en_GB-alan-low' },
|
|
63
|
+
{ name: 'English (UK) - Alan (Male) - Medium', value: 'en_GB-alan-medium' },
|
|
64
|
+
{ name: 'English (UK) - Southern Female - Low', value: 'en_GB-southern_english_female-low' },
|
|
65
|
+
{ name: 'French - Siwis (Female) - Low', value: 'fr_FR-siwis-low' },
|
|
66
|
+
{ name: 'French - Siwis (Female) - Medium', value: 'fr_FR-siwis-medium' },
|
|
67
|
+
{ name: 'Spanish (Spain) - Sharvard (Male) - Medium', value: 'es_ES-sharvard-medium' },
|
|
68
|
+
{ name: 'Spanish (Mexico) - Aldone (Male) - Medium', value: 'es_MX-aldona-medium' },
|
|
69
|
+
{ name: 'German - Eva (Female) - High', value: 'de_DE-eva_k-x_low' },
|
|
70
|
+
{ name: 'German - Thorsten (Male) - High', value: 'de_DE-thorsten-high' },
|
|
71
|
+
{ name: 'German - Thorsten (Male) - Medium', value: 'de_DE-thorsten-medium' },
|
|
72
|
+
{ name: 'German - Thorsten (Male) - Low', value: 'de_DE-thorsten-low' },
|
|
73
|
+
];
|
|
50
74
|
const EDGE_URL = 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4';
|
|
51
75
|
const EDGE_VOICES = [
|
|
52
76
|
{ name: 'Arabic (Egypt) - Salma', value: 'ar-EG-SalmaNeural' },
|
|
@@ -217,17 +241,35 @@ class TTSBigBoss {
|
|
|
217
241
|
description: 'Binary property name containing the reference audio for cloning. Use placeholder "{reference_audio}" in command.',
|
|
218
242
|
},
|
|
219
243
|
{
|
|
220
|
-
displayName: 'Piper Voice Model
|
|
244
|
+
displayName: 'Piper Voice Model',
|
|
221
245
|
name: 'piperModel',
|
|
222
|
-
type: '
|
|
246
|
+
type: 'options',
|
|
247
|
+
options: [
|
|
248
|
+
...PIPER_MODELS,
|
|
249
|
+
{ name: 'Custom (Enter URL)', value: 'custom' },
|
|
250
|
+
],
|
|
223
251
|
default: 'en_US-lessac-medium',
|
|
224
|
-
description: 'Enter a known Piper model name (e.g. "en_US-lessac-medium") which will be auto-downloaded, OR a full URL to the .onnx file.',
|
|
225
252
|
displayOptions: {
|
|
226
253
|
show: {
|
|
227
254
|
engine: ['piper_local'],
|
|
228
255
|
},
|
|
229
256
|
},
|
|
230
|
-
|
|
257
|
+
description: 'Select a voice model. It will be downloaded automatically. Note: Official Piper Arabic only has "Kareem" (Male). For Female Arabic, please use the "Edge TTS" engine.',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
displayName: 'Custom Model Name/URL',
|
|
261
|
+
name: 'piperModelCustom',
|
|
262
|
+
type: 'string',
|
|
263
|
+
default: '',
|
|
264
|
+
placeholder: 'e.g. en_US-bryce-medium',
|
|
265
|
+
displayOptions: {
|
|
266
|
+
show: {
|
|
267
|
+
engine: ['piper_local'],
|
|
268
|
+
piperModel: ['custom'],
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
description: 'Name from Hugging Face (e.g. en_US-bryce-medium) or full URL to .onnx file.',
|
|
272
|
+
},
|
|
231
273
|
],
|
|
232
274
|
};
|
|
233
275
|
}
|
|
@@ -264,7 +306,10 @@ class TTSBigBoss {
|
|
|
264
306
|
}
|
|
265
307
|
}
|
|
266
308
|
else if (engine === 'piper_local') {
|
|
267
|
-
|
|
309
|
+
let piperModel = this.getNodeParameter('piperModel', i);
|
|
310
|
+
if (piperModel === 'custom') {
|
|
311
|
+
piperModel = this.getNodeParameter('piperModelCustom', i);
|
|
312
|
+
}
|
|
268
313
|
const piperBinPath = await ensurePiperBinary(binDir);
|
|
269
314
|
const { modelPath, configPath } = await ensurePiperModel(binDir, piperModel);
|
|
270
315
|
const outFile = path.join(tempDir, `piper_out_${(0, uuid_1.v4)()}.wav`);
|
|
@@ -281,8 +326,12 @@ class TTSBigBoss {
|
|
|
281
326
|
piperProc.on('close', (code) => {
|
|
282
327
|
if (code === 0)
|
|
283
328
|
resolve();
|
|
284
|
-
|
|
329
|
+
if (errData.includes('json.exception.parse_error')) {
|
|
330
|
+
reject(new Error(`Piper Config Error: The downloaded JSON configuration for model '${piperModel}' seems corrupted (HTML instead of JSON?). Try deleting the file at ${configPath} and running again.`));
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
285
333
|
reject(new Error(`Piper failed (exit ${code}): ${errData}`));
|
|
334
|
+
}
|
|
286
335
|
});
|
|
287
336
|
piperProc.on('error', (err) => reject(err));
|
|
288
337
|
});
|
|
@@ -522,30 +571,48 @@ async function ensurePiperBinary(binDir) {
|
|
|
522
571
|
}
|
|
523
572
|
}
|
|
524
573
|
async function ensurePiperModel(binDir, modelNameOrUrl) {
|
|
574
|
+
let modelUrl = '';
|
|
575
|
+
let modelFilename = '';
|
|
525
576
|
if (modelNameOrUrl.startsWith('http')) {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
577
|
+
modelUrl = modelNameOrUrl;
|
|
578
|
+
modelFilename = path.basename(modelNameOrUrl);
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
const parts = modelNameOrUrl.split('-');
|
|
582
|
+
if (parts.length >= 3) {
|
|
583
|
+
const langRegion = parts[0] + '_' + parts[1];
|
|
584
|
+
const voice = parts[2];
|
|
585
|
+
const quality = parts[3] || 'medium';
|
|
586
|
+
const lang = parts[0];
|
|
587
|
+
modelFilename = modelNameOrUrl + '.onnx';
|
|
588
|
+
modelUrl = `https://huggingface.co/rhasspy/piper-voices/resolve/main/${lang}/${langRegion}/${voice}/${quality}/${modelFilename}?download=true`;
|
|
530
589
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if (!fs.existsSync(configPath)) {
|
|
534
|
-
await downloadFile(configUrl, configPath);
|
|
590
|
+
else {
|
|
591
|
+
throw new Error(`Invalid model name format: ${modelNameOrUrl}. Use format lang_REGION-voice-quality`);
|
|
535
592
|
}
|
|
536
|
-
return { modelPath, configPath };
|
|
537
593
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
594
|
+
const modelPath = path.join(binDir, modelFilename);
|
|
595
|
+
const configPath = modelPath + '.json';
|
|
596
|
+
if (!fs.existsSync(modelPath) || fs.statSync(modelPath).size < 1000) {
|
|
597
|
+
console.log(`Downloading Piper Model: ${modelUrl}`);
|
|
598
|
+
await downloadFile(modelUrl, modelPath);
|
|
599
|
+
}
|
|
600
|
+
if (!fs.existsSync(configPath) || fs.statSync(configPath).size < 10) {
|
|
601
|
+
const configUrl = modelUrl + '.json';
|
|
602
|
+
console.log(`Downloading Piper Config: ${configUrl}`);
|
|
603
|
+
await downloadFile(configUrl, configPath);
|
|
604
|
+
try {
|
|
605
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
606
|
+
JSON.parse(content);
|
|
607
|
+
}
|
|
608
|
+
catch (e) {
|
|
609
|
+
fs.unlinkSync(configPath);
|
|
610
|
+
if (fs.existsSync(modelPath))
|
|
611
|
+
fs.unlinkSync(modelPath);
|
|
612
|
+
throw new Error(`Downloaded config for ${modelNameOrUrl} was not valid JSON. URL might be wrong: ${configUrl}. Content start: ${fs.readFileSync(configPath, 'utf8').substring(0, 50)}...`);
|
|
613
|
+
}
|
|
547
614
|
}
|
|
548
|
-
return { modelPath
|
|
615
|
+
return { modelPath, configPath };
|
|
549
616
|
}
|
|
550
617
|
async function downloadFile(url, dest) {
|
|
551
618
|
return new Promise((resolve, reject) => {
|
|
@@ -17,6 +17,45 @@ import * as zlib from 'zlib'; // For extracting .tar.gz if needed, typically usa
|
|
|
17
17
|
|
|
18
18
|
const pipeline = promisify(stream.pipeline);
|
|
19
19
|
|
|
20
|
+
// Piper Models List (Curated High Quality)
|
|
21
|
+
// Note: Official Piper repo currently only has 'kareem' (Male) for Arabic.
|
|
22
|
+
// For Female Arabic voices, please use the 'Edge TTS' engine (Salma, Zariyah).
|
|
23
|
+
const PIPER_MODELS = [
|
|
24
|
+
// Arabic
|
|
25
|
+
{ name: 'Arabic (Jordan) - Kareem (Male) - Low', value: 'ar_JO-kareem-low' },
|
|
26
|
+
{ name: 'Arabic (Jordan) - Kareem (Male) - Medium', value: 'ar_JO-kareem-medium' },
|
|
27
|
+
|
|
28
|
+
// English (US)
|
|
29
|
+
{ name: 'English (US) - Lessac (Female) - Low', value: 'en_US-lessac-low' },
|
|
30
|
+
{ name: 'English (US) - Lessac (Female) - Medium', value: 'en_US-lessac-medium' },
|
|
31
|
+
{ name: 'English (US) - Lessac (Female) - High', value: 'en_US-lessac-high' },
|
|
32
|
+
{ name: 'English (US) - Ryan (Male) - Low', value: 'en_US-ryan-low' },
|
|
33
|
+
{ name: 'English (US) - Ryan (Male) - Medium', value: 'en_US-ryan-medium' },
|
|
34
|
+
{ name: 'English (US) - Ryan (Male) - High', value: 'en_US-ryan-high' },
|
|
35
|
+
{ name: 'English (US) - Amy (Female) - Low', value: 'en_US-amy-low' },
|
|
36
|
+
{ name: 'English (US) - Amy (Female) - Medium', value: 'en_US-amy-medium' },
|
|
37
|
+
{ name: 'English (US) - Kathleen (Female) - Low', value: 'en_US-kathleen-low' },
|
|
38
|
+
|
|
39
|
+
// English (UK)
|
|
40
|
+
{ name: 'English (UK) - Alan (Male) - Low', value: 'en_GB-alan-low' },
|
|
41
|
+
{ name: 'English (UK) - Alan (Male) - Medium', value: 'en_GB-alan-medium' },
|
|
42
|
+
{ name: 'English (UK) - Southern Female - Low', value: 'en_GB-southern_english_female-low' },
|
|
43
|
+
|
|
44
|
+
// French
|
|
45
|
+
{ name: 'French - Siwis (Female) - Low', value: 'fr_FR-siwis-low' },
|
|
46
|
+
{ name: 'French - Siwis (Female) - Medium', value: 'fr_FR-siwis-medium' },
|
|
47
|
+
|
|
48
|
+
// Spanish
|
|
49
|
+
{ name: 'Spanish (Spain) - Sharvard (Male) - Medium', value: 'es_ES-sharvard-medium' },
|
|
50
|
+
{ name: 'Spanish (Mexico) - Aldone (Male) - Medium', value: 'es_MX-aldona-medium' },
|
|
51
|
+
|
|
52
|
+
// German
|
|
53
|
+
{ name: 'German - Eva (Female) - High', value: 'de_DE-eva_k-x_low' },
|
|
54
|
+
{ name: 'German - Thorsten (Male) - High', value: 'de_DE-thorsten-high' },
|
|
55
|
+
{ name: 'German - Thorsten (Male) - Medium', value: 'de_DE-thorsten-medium' },
|
|
56
|
+
{ name: 'German - Thorsten (Male) - Low', value: 'de_DE-thorsten-low' },
|
|
57
|
+
];
|
|
58
|
+
|
|
20
59
|
// Edge TTS Constants
|
|
21
60
|
const EDGE_URL = 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=6A5AA1D4EAFF4E9FB37E23D68491D6F4';
|
|
22
61
|
const EDGE_VOICES = [
|
|
@@ -212,17 +251,35 @@ export class TTSBigBoss implements INodeType {
|
|
|
212
251
|
// Local Piper Settings
|
|
213
252
|
// ----------------------------------
|
|
214
253
|
{
|
|
215
|
-
displayName: 'Piper Voice Model
|
|
254
|
+
displayName: 'Piper Voice Model',
|
|
216
255
|
name: 'piperModel',
|
|
217
|
-
type: '
|
|
256
|
+
type: 'options',
|
|
257
|
+
options: [
|
|
258
|
+
...PIPER_MODELS,
|
|
259
|
+
{ name: 'Custom (Enter URL)', value: 'custom' },
|
|
260
|
+
],
|
|
218
261
|
default: 'en_US-lessac-medium',
|
|
219
|
-
description: 'Enter a known Piper model name (e.g. "en_US-lessac-medium") which will be auto-downloaded, OR a full URL to the .onnx file.',
|
|
220
262
|
displayOptions: {
|
|
221
263
|
show: {
|
|
222
264
|
engine: ['piper_local'],
|
|
223
265
|
},
|
|
224
266
|
},
|
|
225
|
-
|
|
267
|
+
description: 'Select a voice model. It will be downloaded automatically. Note: Official Piper Arabic only has "Kareem" (Male). For Female Arabic, please use the "Edge TTS" engine.',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
displayName: 'Custom Model Name/URL',
|
|
271
|
+
name: 'piperModelCustom',
|
|
272
|
+
type: 'string',
|
|
273
|
+
default: '',
|
|
274
|
+
placeholder: 'e.g. en_US-bryce-medium',
|
|
275
|
+
displayOptions: {
|
|
276
|
+
show: {
|
|
277
|
+
engine: ['piper_local'],
|
|
278
|
+
piperModel: ['custom'],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
description: 'Name from Hugging Face (e.g. en_US-bryce-medium) or full URL to .onnx file.',
|
|
282
|
+
},
|
|
226
283
|
],
|
|
227
284
|
};
|
|
228
285
|
|
|
@@ -271,7 +328,10 @@ export class TTSBigBoss implements INodeType {
|
|
|
271
328
|
// ----------------------------------
|
|
272
329
|
// PIPER LOCAL AUTOMATION
|
|
273
330
|
// ----------------------------------
|
|
274
|
-
|
|
331
|
+
let piperModel = this.getNodeParameter('piperModel', i) as string;
|
|
332
|
+
if (piperModel === 'custom') {
|
|
333
|
+
piperModel = this.getNodeParameter('piperModelCustom', i) as string;
|
|
334
|
+
}
|
|
275
335
|
|
|
276
336
|
// 1. Ensure Piper Binary
|
|
277
337
|
const piperBinPath = await ensurePiperBinary(binDir);
|
|
@@ -299,7 +359,12 @@ export class TTSBigBoss implements INodeType {
|
|
|
299
359
|
|
|
300
360
|
piperProc.on('close', (code) => {
|
|
301
361
|
if (code === 0) resolve();
|
|
302
|
-
|
|
362
|
+
// Check for the specific JSON error in stderr
|
|
363
|
+
if (errData.includes('json.exception.parse_error')) {
|
|
364
|
+
reject(new Error(`Piper Config Error: The downloaded JSON configuration for model '${piperModel}' seems corrupted (HTML instead of JSON?). Try deleting the file at ${configPath} and running again.`));
|
|
365
|
+
} else {
|
|
366
|
+
reject(new Error(`Piper failed (exit ${code}): ${errData}`));
|
|
367
|
+
}
|
|
303
368
|
});
|
|
304
369
|
|
|
305
370
|
piperProc.on('error', (err) => reject(err));
|
|
@@ -641,42 +706,66 @@ async function ensurePiperBinary(binDir: string): Promise<string> {
|
|
|
641
706
|
}
|
|
642
707
|
|
|
643
708
|
async function ensurePiperModel(binDir: string, modelNameOrUrl: string): Promise<{ modelPath: string, configPath: string }> {
|
|
644
|
-
//
|
|
645
|
-
// https://huggingface.co/rhasspy/piper-voices/resolve/main/
|
|
646
|
-
//
|
|
647
|
-
// We'll rely on a direct URL or a simplied map for BigBoss defaults.
|
|
709
|
+
// Heuristic for HF URL construction
|
|
710
|
+
// https://huggingface.co/rhasspy/piper-voices/resolve/main/[lang_code]/[region]/[voice]/[quality]/[filename]
|
|
711
|
+
// Example: en_US-lessac-medium -> en/en_US/lessac/medium/en_US-lessac-medium.onnx
|
|
648
712
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const fileName = path.basename(modelNameOrUrl);
|
|
652
|
-
const modelPath = path.join(binDir, fileName);
|
|
653
|
-
if (!fs.existsSync(modelPath)) {
|
|
654
|
-
await downloadFile(modelNameOrUrl, modelPath);
|
|
655
|
-
}
|
|
713
|
+
let modelUrl = '';
|
|
714
|
+
let modelFilename = '';
|
|
656
715
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
716
|
+
if (modelNameOrUrl.startsWith('http')) {
|
|
717
|
+
modelUrl = modelNameOrUrl;
|
|
718
|
+
modelFilename = path.basename(modelNameOrUrl);
|
|
719
|
+
} else {
|
|
720
|
+
// Construct URL from name
|
|
721
|
+
const parts = modelNameOrUrl.split('-');
|
|
722
|
+
if (parts.length >= 3) {
|
|
723
|
+
const langRegion = parts[0] + '_' + parts[1]; // en_US
|
|
724
|
+
const voice = parts[2];
|
|
725
|
+
const quality = parts[3] || 'medium';
|
|
726
|
+
const lang = parts[0]; // en
|
|
727
|
+
|
|
728
|
+
// e.g. en_US-lessac-medium
|
|
729
|
+
// lang=en, region=en_US, voice=lessac, quality=medium
|
|
730
|
+
// url path: en/en_US/lessac/medium/en_US-lessac-medium.onnx
|
|
731
|
+
|
|
732
|
+
// Handle special case: ar_JO (no lang folder? check repo)
|
|
733
|
+
// Generally structure is: lang_short/lang_long/voice/quality/filename
|
|
734
|
+
|
|
735
|
+
modelFilename = modelNameOrUrl + '.onnx';
|
|
736
|
+
modelUrl = `https://huggingface.co/rhasspy/piper-voices/resolve/main/${lang}/${langRegion}/${voice}/${quality}/${modelFilename}?download=true`; // Add download=true to force direct link
|
|
737
|
+
} else {
|
|
738
|
+
throw new Error(`Invalid model name format: ${modelNameOrUrl}. Use format lang_REGION-voice-quality`);
|
|
661
739
|
}
|
|
662
|
-
return { modelPath, configPath };
|
|
663
740
|
}
|
|
664
741
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
if (modelNameOrUrl === 'en_US-lessac-medium') {
|
|
668
|
-
const url = 'https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx';
|
|
669
|
-
const modelPath = path.join(binDir, 'en_US-lessac-medium.onnx');
|
|
670
|
-
const configPath = path.join(binDir, 'en_US-lessac-medium.onnx.json');
|
|
742
|
+
const modelPath = path.join(binDir, modelFilename);
|
|
743
|
+
const configPath = modelPath + '.json';
|
|
671
744
|
|
|
672
|
-
|
|
673
|
-
|
|
745
|
+
// Download if missing or seems incomplete (size < 1KB)
|
|
746
|
+
if (!fs.existsSync(modelPath) || fs.statSync(modelPath).size < 1000) {
|
|
747
|
+
console.log(`Downloading Piper Model: ${modelUrl}`);
|
|
748
|
+
await downloadFile(modelUrl, modelPath);
|
|
749
|
+
}
|
|
674
750
|
|
|
675
|
-
|
|
751
|
+
// Download config if missing or seems incomplete (size < 10 bytes)
|
|
752
|
+
if (!fs.existsSync(configPath) || fs.statSync(configPath).size < 10) {
|
|
753
|
+
const configUrl = modelUrl + '.json';
|
|
754
|
+
console.log(`Downloading Piper Config: ${configUrl}`);
|
|
755
|
+
await downloadFile(configUrl, configPath);
|
|
756
|
+
|
|
757
|
+
// Validate JSON
|
|
758
|
+
try {
|
|
759
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
760
|
+
JSON.parse(content);
|
|
761
|
+
} catch (e) {
|
|
762
|
+
fs.unlinkSync(configPath); // Delete bad file
|
|
763
|
+
if (fs.existsSync(modelPath)) fs.unlinkSync(modelPath); // Delete model too as it might be bad
|
|
764
|
+
throw new Error(`Downloaded config for ${modelNameOrUrl} was not valid JSON. URL might be wrong: ${configUrl}. Content start: ${fs.readFileSync(configPath, 'utf8').substring(0, 50)}...`);
|
|
765
|
+
}
|
|
676
766
|
}
|
|
677
767
|
|
|
678
|
-
|
|
679
|
-
return { modelPath: modelNameOrUrl, configPath: modelNameOrUrl + '.json' };
|
|
768
|
+
return { modelPath, configPath };
|
|
680
769
|
}
|
|
681
770
|
|
|
682
771
|
async function downloadFile(url: string, dest: string): Promise<void> {
|