mia-narrative 1.0.10 → 1.1.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/README.md CHANGED
@@ -31,49 +31,49 @@
31
31
 
32
32
  ### Get Started
33
33
 
34
- ```bash
34
+ \`\`\`bash
35
35
  git clone <repo>
36
36
  cd cli
37
37
  npm install
38
38
  npm run build
39
39
  npm link # Makes 'mia-narrative' available globally
40
- ```
40
+ \`\`\`
41
41
 
42
42
  ### Optional: Setup Professional Voices
43
43
 
44
- ```bash
44
+ \`\`\`bash
45
45
  npm run setup
46
46
  # Automatically downloads Piper models (one-time, ~380MB)
47
- ```
47
+ \`\`\`
48
48
 
49
49
  ### Optional: ElevenLabs (Cloud Voices)
50
50
 
51
51
  Get an API key from [elevenlabs.io](https://elevenlabs.io):
52
52
 
53
- ```bash
53
+ \`\`\`bash
54
54
  export ELEVENLABS_API_KEY="your_key_here"
55
55
  mia-narrative g -E elevenlabs -T "Hello" -O output.mp3
56
- ```
56
+ \`\`\`
57
57
 
58
58
  ## Quick Start
59
59
 
60
60
  **Simplest command** (uses system voice):
61
- ```bash
61
+ \`\`\`bash
62
62
  mia-narrative g -T "Hello, world!"
63
- ```
63
+ \`\`\`
64
64
 
65
65
  **With a specific voice** (Miette with British accent):
66
- ```bash
66
+ \`\`\`bash
67
67
  mia-narrative g -E piper -v miette -T "What a lovely day!" -O story.mp3
68
- ```
68
+ \`\`\`
69
69
 
70
70
  **From a text file** with effects:
71
- ```bash
71
+ \`\`\`bash
72
72
  mia-narrative g -E piper -v mia -F chapter.txt --pitch 1.1 --speed 0.95 -O chapter.mp3
73
- ```
73
+ \`\`\`
74
74
 
75
75
  **Professional narrator** with full effects:
76
- ```bash
76
+ \`\`\`bash
77
77
  mia-narrative g \
78
78
  -E piper \
79
79
  -v mia \
@@ -83,7 +83,7 @@ mia-narrative g \
83
83
  --reverb 0.3 \
84
84
  --compression 0.4 \
85
85
  -O final.mp3
86
- ```
86
+ \`\`\`
87
87
 
88
88
  ### Command & Option Reference
89
89
 
@@ -98,7 +98,7 @@ mia-narrative g \
98
98
 
99
99
  See which narrators are available:
100
100
 
101
- ```bash
101
+ \`\`\`bash
102
102
  # See system voices (quick, no setup)
103
103
  mia-narrative voices --engine system
104
104
 
@@ -107,7 +107,7 @@ mia-narrative voices --engine piper
107
107
 
108
108
  # See ElevenLabs options (requires API key)
109
109
  mia-narrative voices --engine elevenlabs
110
- ```
110
+ \`\`\`
111
111
 
112
112
  Output shows voice ID, personality, and which model it uses.
113
113
 
@@ -115,10 +115,10 @@ Output shows voice ID, personality, and which model it uses.
115
115
 
116
116
  Get access to professional voices with full control:
117
117
 
118
- ```bash
118
+ \`\`\`bash
119
119
  npm run setup
120
120
  # Downloads Mia, Miette, Seraphine, Jeremy, ResoNova, Zephyr, Echo, Atlas
121
- ```
121
+ \`\`\`
122
122
 
123
123
  ## Audio Parameters
124
124
 
@@ -153,8 +153,8 @@ npm run setup
153
153
 
154
154
  ## Contributing & Development
155
155
 
156
- ```bash
156
+ \`\`\`bash
157
157
  npm run build # Compile TypeScript
158
158
  npm run dev -- g -T "test" # Run in dev mode
159
159
  npm test # Run tests
160
- ```
160
+ \`\`\`
@@ -6,6 +6,7 @@ import { join, dirname } from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { generateCommand } from '../src/commands/generate.js';
8
8
  import { voicesCommand } from '../src/commands/voices.js';
9
+ import { ensureMpg123 } from '../src/utils/prerequisites.js';
9
10
 
10
11
  // Get version from package.json
11
12
  let version = '1.0.0'; // fallback
@@ -20,6 +21,9 @@ try {
20
21
  // If we can't read package.json, use fallback version
21
22
  }
22
23
 
24
+ // Check for mpg123 on startup (try to install if missing)
25
+ ensureMpg123();
26
+
23
27
  const program = new Command();
24
28
 
25
29
  program
@@ -5,6 +5,7 @@ import { join, dirname } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { generateCommand } from '../src/commands/generate.js';
7
7
  import { voicesCommand } from '../src/commands/voices.js';
8
+ import { ensureMpg123 } from '../src/utils/prerequisites.js';
8
9
  // Get version from package.json
9
10
  let version = '1.0.0'; // fallback
10
11
  try {
@@ -18,6 +19,8 @@ try {
18
19
  catch {
19
20
  // If we can't read package.json, use fallback version
20
21
  }
22
+ // Check for mpg123 on startup (try to install if missing)
23
+ ensureMpg123();
21
24
  const program = new Command();
22
25
  program
23
26
  .name('mia-narrative')
@@ -1 +1 @@
1
- {"version":3,"file":"mia-narrative.js","sourceRoot":"","sources":["../../bin/mia-narrative.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,gCAAgC;AAChC,IAAI,OAAO,GAAG,OAAO,CAAC,CAAC,WAAW;AAClC,IAAI,CAAC;IACH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,oDAAoD;IACpD,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;AAChC,CAAC;AAAC,MAAM,CAAC;IACP,sDAAsD;AACxD,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,KAAK,CAAC,GAAG,CAAC;KACV,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,mBAAmB,EAAE,2BAA2B,CAAC;KACxD,MAAM,CAAC,mBAAmB,EAAE,qBAAqB,CAAC;KAClD,MAAM,CACL,qBAAqB,EACrB,+CAA+C,EAC/C,QAAQ,CACT;KACA,MAAM,CAAC,oBAAoB,EAAE,iBAAiB,CAAC;KAC/C,MAAM,CACL,qBAAqB,EACrB,4CAA4C,CAC7C;KACA,MAAM,CACL,kBAAkB,EAClB,4BAA4B,EAC5B,UAAU,CACX;KACA,MAAM,CACL,kBAAkB,EAClB,sBAAsB,EACtB,UAAU,CACX;KACA,MAAM,CACL,mBAAmB,EACnB,uBAAuB,EACvB,UAAU,CACX;KACA,MAAM,CACL,qBAAqB,EACrB,yBAAyB,EACzB,UAAU,CACX;KACA,MAAM,CACL,mBAAmB,EACnB,uBAAuB,EACvB,UAAU,CACX;KACA,MAAM,CACL,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,CACX;KACA,MAAM,CACL,wBAAwB,EACxB,4BAA4B,EAC5B,UAAU,CACX;KACA,MAAM,CACL,kBAAkB,EAClB,6BAA6B,EAC7B,UAAU,CACX;KACA,MAAM,CAAC,eAAe,CAAC,CAAC;AAE3B,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CACL,iBAAiB,EACjB,2DAA2D,EAC3D,QAAQ,CACT;KACA,MAAM,CACL,mBAAmB,EACnB,mCAAmC,EACnC,OAAO,CACR;KACA,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"mia-narrative.js","sourceRoot":"","sources":["../../bin/mia-narrative.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,gCAAgC;AAChC,IAAI,OAAO,GAAG,OAAO,CAAC,CAAC,WAAW;AAClC,IAAI,CAAC;IACH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,oDAAoD;IACpD,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;AAChC,CAAC;AAAC,MAAM,CAAC;IACP,sDAAsD;AACxD,CAAC;AAED,0DAA0D;AAC1D,YAAY,EAAE,CAAC;AAEf,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,KAAK,CAAC,GAAG,CAAC;KACV,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,mBAAmB,EAAE,2BAA2B,CAAC;KACxD,MAAM,CAAC,mBAAmB,EAAE,qBAAqB,CAAC;KAClD,MAAM,CACL,qBAAqB,EACrB,+CAA+C,EAC/C,QAAQ,CACT;KACA,MAAM,CAAC,oBAAoB,EAAE,iBAAiB,CAAC;KAC/C,MAAM,CACL,qBAAqB,EACrB,4CAA4C,CAC7C;KACA,MAAM,CACL,kBAAkB,EAClB,4BAA4B,EAC5B,UAAU,CACX;KACA,MAAM,CACL,kBAAkB,EAClB,sBAAsB,EACtB,UAAU,CACX;KACA,MAAM,CACL,mBAAmB,EACnB,uBAAuB,EACvB,UAAU,CACX;KACA,MAAM,CACL,qBAAqB,EACrB,yBAAyB,EACzB,UAAU,CACX;KACA,MAAM,CACL,mBAAmB,EACnB,uBAAuB,EACvB,UAAU,CACX;KACA,MAAM,CACL,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,CACX;KACA,MAAM,CACL,wBAAwB,EACxB,4BAA4B,EAC5B,UAAU,CACX;KACA,MAAM,CACL,kBAAkB,EAClB,6BAA6B,EAC7B,UAAU,CACX;KACA,MAAM,CAAC,eAAe,CAAC,CAAC;AAE3B,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CACL,iBAAiB,EACjB,2DAA2D,EAC3D,QAAQ,CACT;KACA,MAAM,CACL,mBAAmB,EACnB,mCAAmC,EACnC,OAAO,CACR;KACA,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"piper.d.ts","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGpF,qBAAa,WAAY,SAAQ,SAAS;IACxC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,SAAS,CAAc;IAEzB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD,OAAO,CAAC,cAAc;IAMhB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAmD7D,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAe7B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBrC,OAAO,IAAI,MAAM;IAIjB,OAAO,CAAC,QAAQ;CA4CjB"}
1
+ {"version":3,"file":"piper.d.ts","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAIpF,qBAAa,WAAY,SAAQ,SAAS;IACxC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,SAAS,CAAc;IAEzB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxD,OAAO,CAAC,cAAc;IAMhB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAyD7D,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAiB7B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAerC,OAAO,IAAI,MAAM;IAIjB,OAAO,CAAC,QAAQ;CA4CjB"}
@@ -1,11 +1,12 @@
1
1
  import { spawn, execSync } from 'child_process';
2
2
  import { writeFileSync, unlinkSync, readFileSync, existsSync } from 'fs';
3
- import { tmpdir, homedir } from 'os';
3
+ import { tmpdir } from 'os';
4
4
  import { join } from 'path';
5
5
  import { randomUUID } from 'crypto';
6
6
  import { Logger } from '../utils/logger.js';
7
7
  import { TTSEngine } from './base.js';
8
8
  import { getVoiceProfile, VOICE_PROFILES } from '../config/voices.js';
9
+ import { getPiperPath, getModelPath, getPiperMissingMessage } from '../utils/prerequisites.js';
9
10
  export class PiperEngine extends TTSEngine {
10
11
  constructor() {
11
12
  super(...arguments);
@@ -13,16 +14,15 @@ export class PiperEngine extends TTSEngine {
13
14
  this.modelPath = '';
14
15
  }
15
16
  async initialize(config) {
16
- if (config.piperPath) {
17
- this.piperPath = config.piperPath;
18
- }
17
+ // Use provided config or fall back to environment/auto-detection
18
+ this.piperPath = config.piperPath || getPiperPath();
19
19
  if (config.modelPath) {
20
20
  this.modelPath = config.modelPath;
21
21
  }
22
22
  else {
23
- this.modelPath = process.env.MIA_NARRATIVE_PIPER_MODEL || '';
23
+ this.modelPath = getModelPath();
24
24
  }
25
- Logger.debug(`Piper engine initialized with model path: ${this.modelPath}`);
25
+ Logger.debug(`Piper engine initialized with piper path: ${this.piperPath}, model path: ${this.modelPath}`);
26
26
  }
27
27
  preprocessText(text) {
28
28
  // Add line breaks after periods to create longer pauses
@@ -38,13 +38,20 @@ export class PiperEngine extends TTSEngine {
38
38
  if (!text || text.trim().length === 0) {
39
39
  throw new Error('Text cannot be empty');
40
40
  }
41
- // Build path to model file
42
- const modelDir = join(homedir(), '.local/share/piper-tts');
43
- const modelFile = join(modelDir, `${voiceProfile.piperModel}.onnx`);
41
+ // Build path to model file using configured model path
42
+ let modelFile;
43
+ if (this.modelPath.endsWith('.onnx')) {
44
+ // If modelPath is already a full model path, use it directly
45
+ modelFile = this.modelPath;
46
+ }
47
+ else {
48
+ // Otherwise, treat it as a directory and append model filename
49
+ modelFile = join(this.modelPath, `${voiceProfile.piperModel}.onnx`);
50
+ }
44
51
  if (!existsSync(modelFile)) {
45
52
  throw new Error(`Piper model not found: ${voiceProfile.piperModel}\n` +
46
53
  `Expected location: ${modelFile}\n` +
47
- `Run 'npm run setup' to download models.`);
54
+ `Run 'npm run setup' to download models or set MIA_NARRATIVE_PIPER_MODEL environment variable.`);
48
55
  }
49
56
  const tempDir = tmpdir();
50
57
  const uid = randomUUID();
@@ -75,7 +82,9 @@ export class PiperEngine extends TTSEngine {
75
82
  }
76
83
  }
77
84
  async getVoices() {
78
- const modelDir = join(homedir(), '.local/share/piper-tts');
85
+ const modelDir = this.modelPath.endsWith('.onnx')
86
+ ? this.modelPath.substring(0, this.modelPath.lastIndexOf('/'))
87
+ : this.modelPath;
79
88
  return Object.values(VOICE_PROFILES)
80
89
  .filter(v => {
81
90
  const modelFile = join(modelDir, `${v.piperModel}.onnx`);
@@ -93,7 +102,6 @@ export class PiperEngine extends TTSEngine {
93
102
  execSync('which piper', { stdio: 'pipe' });
94
103
  }
95
104
  else {
96
- const { existsSync } = await import('fs');
97
105
  if (!existsSync(this.piperPath)) {
98
106
  return false;
99
107
  }
@@ -134,7 +142,7 @@ export class PiperEngine extends TTSEngine {
134
142
  }
135
143
  });
136
144
  piper.on('error', (error) => {
137
- reject(new Error(`Failed to start Piper: ${error.message}. Ensure Piper is installed and in your PATH.`));
145
+ reject(new Error(`Failed to start Piper: ${error.message}\n\n${getPiperMissingMessage()}`));
138
146
  });
139
147
  });
140
148
  }
@@ -1 +1 @@
1
- {"version":3,"file":"piper.js","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAgD,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,OAAO,WAAY,SAAQ,SAAS;IAA1C;;QACU,cAAS,GAAW,OAAO,CAAC;QAC5B,cAAS,GAAW,EAAE,CAAC;IAwJjC,CAAC;IAtJC,KAAK,CAAC,UAAU,CAAC,MAAuB;QACtC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACpC,CAAC;QAED,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,EAAE,CAAC;QAC/D,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,6CAA6C,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9E,CAAC;IAEO,cAAc,CAAC,IAAY;QACjC,wDAAwD;QACxD,iDAAiD;QACjD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAA6B;QAC/C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAElC,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,gBAAgB,OAAO,cAAc,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC1C,CAAC;QAED,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,wBAAwB,CAAC,CAAC;QAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,YAAY,CAAC,UAAU,OAAO,CAAC,CAAC;QAEpE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,0BAA0B,YAAY,CAAC,UAAU,IAAI;gBACrD,sBAAsB,SAAS,IAAI;gBACnC,yCAAyC,CAC1C,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAAC,CAAC;QAE3D,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAChD,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;YAChD,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YACpD,MAAM,WAAW,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YAC5C,OAAO,WAAW,CAAC;QACrB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC;gBACH,UAAU,CAAC,QAAQ,CAAC,CAAC;gBACrB,MAAM,CAAC,KAAK,CAAC,eAAe,QAAQ,EAAE,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,UAAU,CAAC,SAAS,CAAC,CAAC;gBACtB,MAAM,CAAC,KAAK,CAAC,eAAe,SAAS,EAAE,CAAC,CAAC;YAC3C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,wBAAwB,CAAC,CAAC;QAE3D,OAAO,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC;aACjC,MAAM,CAAC,CAAC,CAAC,EAAE;YACV,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,UAAU,OAAO,CAAC,CAAC;YACzD,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;QAC/B,CAAC,CAAC;aACD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACT,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,GAAG;YAC9B,KAAK,EAAE,CAAC,CAAC,UAAU;SACpB,CAAC,CAAC,CAAC;IACR,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;gBAC/B,QAAQ,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;oBAChC,OAAO,KAAK,CAAC;gBACf,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,QAAQ,CACd,QAAgB,EAChB,UAAkB,EAClB,KAAa;QAEb,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG;gBACX,SAAS;gBACT,KAAK;gBACL,eAAe;gBACf,UAAU;aACX,CAAC;YAEF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YAEjF,MAAM,WAAW,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACpD,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC/B,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YAElB,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,MAAM,eAAe,GAAG,IAAI,CAAC;YAE7B,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC9B,MAAM,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;YAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBACzB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,EAAE,CAAC;gBACZ,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,IAAI,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC1B,MAAM,CACJ,IAAI,KAAK,CACP,0BAA0B,KAAK,CAAC,OAAO,+CAA+C,CACvF,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
1
+ {"version":3,"file":"piper.js","sourceRoot":"","sources":["../../../src/engines/piper.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ export declare class FileReader {
3
3
  static fileExists(filePath: string): boolean;
4
4
  /**
5
5
  * Strip Markdown syntax from text, leaving only readable content
6
+ * Also adds strategic pauses for better TTS pacing
6
7
  */
7
8
  static stripMarkdown(text: string): string;
8
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-reader.d.ts","sourceRoot":"","sources":["../../../src/utils/file-reader.ts"],"names":[],"mappings":"AAIA,qBAAa,UAAU;IACrB,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAY7C,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQ5C;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAkD3C"}
1
+ {"version":3,"file":"file-reader.d.ts","sourceRoot":"","sources":["../../../src/utils/file-reader.ts"],"names":[],"mappings":"AAIA,qBAAa,UAAU;IACrB,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAY7C,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQ5C;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CA6D3C"}
@@ -24,9 +24,18 @@ export class FileReader {
24
24
  }
25
25
  /**
26
26
  * Strip Markdown syntax from text, leaving only readable content
27
+ * Also adds strategic pauses for better TTS pacing
27
28
  */
28
29
  static stripMarkdown(text) {
29
30
  let result = text;
31
+ // Add pauses before headers (before stripping syntax)
32
+ // Insert period+newline twice before headers for longer pause
33
+ result = result.replace(/^(#{1,6})\s+/gm, '\n\n. \n. \n$1 ');
34
+ // Add pause after list items (before stripping list markers)
35
+ result = result.replace(/^([\s]*[-*+]\s+.+)$/gm, '$1. ');
36
+ result = result.replace(/^([\s]*\d+\.\s+.+)$/gm, '$1. ');
37
+ // Add pause after colons (in regular text)
38
+ result = result.replace(/:\s+([^\n])/g, ': . $1');
30
39
  // Remove code blocks (``` ... ```)
31
40
  result = result.replace(/```[\s\S]*?```/g, '');
32
41
  // Remove indented code blocks (4 spaces or tab at start of line)
@@ -1 +1 @@
1
- {"version":3,"file":"file-reader.js","sourceRoot":"","sources":["../../../src/utils/file-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,OAAO,UAAU;IACrB,MAAM,CAAC,YAAY,CAAC,QAAgB;QAClC,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;YACvC,OAAO,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,MAAM,CAAC,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACxD,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,QAAgB;QAChC,IAAI,CAAC;YACH,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,IAAY;QAC/B,IAAI,MAAM,GAAG,IAAI,CAAC;QAElB,mCAAmC;QACnC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QAE/C,iEAAiE;QACjE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QAE/C,8BAA8B;QAC9B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAE5C,qCAAqC;QACrC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;QACnD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QAE9C,mCAAmC;QACnC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;QAC/C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAE5C,2BAA2B;QAC3B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,yBAAyB,EAAE,IAAI,CAAC,CAAC;QAEzD,4BAA4B;QAC5B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC;QAExD,+BAA+B;QAC/B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAExC,yBAAyB;QACzB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEvC,6CAA6C;QAC7C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAChD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAEhD,0CAA0C;QAC1C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;QAE1D,mBAAmB;QACnB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAExC,wDAAwD;QACxD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAEjD,kBAAkB;QAClB,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAEvB,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
1
+ {"version":3,"file":"file-reader.js","sourceRoot":"","sources":["../../../src/utils/file-reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ export interface PrerequisiteStatus {
2
+ piperPath: string;
3
+ piperAvailable: boolean;
4
+ modelPath: string;
5
+ modelAvailable: boolean;
6
+ configPath: string;
7
+ configAvailable: boolean;
8
+ }
9
+ /**
10
+ * Get the path to the piper binary
11
+ * Priority: MIA_NARRATIVE_PIPER_PATH env var → which piper → /opt/mianar/bin/piper
12
+ */
13
+ export declare function getPiperPath(): string;
14
+ /**
15
+ * Get the path to piper model file
16
+ * Priority: MIA_NARRATIVE_PIPER_MODEL env var → /opt/mianar/piper-tts/... → ~/.local/share/piper-tts/...
17
+ */
18
+ export declare function getModelPath(): string;
19
+ /**
20
+ * Check piper binary availability
21
+ */
22
+ export declare function isPiperAvailable(): boolean;
23
+ /**
24
+ * Check if model is available
25
+ */
26
+ export declare function isModelAvailable(modelName?: string): boolean;
27
+ /**
28
+ * Get comprehensive prerequisite status
29
+ */
30
+ export declare function getPrerequisiteStatus(): PrerequisiteStatus;
31
+ /**
32
+ * Generate helpful error message when piper is not available
33
+ */
34
+ export declare function getPiperMissingMessage(): string;
35
+ /**
36
+ * Generate helpful error message when models are not available
37
+ */
38
+ export declare function getModelsMissingMessage(): string;
39
+ /**
40
+ * Ensure directory exists for models
41
+ */
42
+ export declare function ensureModelDirectory(): void;
43
+ /**
44
+ * Check if mpg123 (MP3 audio player) is installed
45
+ */
46
+ export declare function isMpg123Available(): boolean;
47
+ /**
48
+ * Generate helpful message about mpg123 installation
49
+ */
50
+ export declare function getMpg123MissingMessage(): string;
51
+ /**
52
+ * Attempt to install mpg123 automatically (Linux only)
53
+ */
54
+ export declare function tryInstallMpg123(): boolean;
55
+ /**
56
+ * Ensure mpg123 is available, try to install if missing
57
+ */
58
+ export declare function ensureMpg123(): boolean;
59
+ //# sourceMappingURL=prerequisites.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prerequisites.d.ts","sourceRoot":"","sources":["../../../src/utils/prerequisites.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAqBrC;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAiBrC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAe1C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,GAAE,MAA8B,GAAG,OAAO,CAMnF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,kBAAkB,CAY1D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAoB/C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAoBhD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAM3C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAO3C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAYhD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAkB1C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAYtC"}
@@ -0,0 +1,218 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, mkdirSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { join } from 'path';
5
+ import { Logger } from './logger.js';
6
+ const OPT_MIANAR_PATH = '/opt/mianar';
7
+ const OPT_MIANAR_PIPER_MODEL = '/opt/mianar/piper-tts/en_US-lessac-medium.onnx';
8
+ /**
9
+ * Get the path to the piper binary
10
+ * Priority: MIA_NARRATIVE_PIPER_PATH env var → which piper → /opt/mianar/bin/piper
11
+ */
12
+ export function getPiperPath() {
13
+ // 1. Check explicit environment variable
14
+ if (process.env.MIA_NARRATIVE_PIPER_PATH) {
15
+ Logger.debug(`Using MIA_NARRATIVE_PIPER_PATH: ${process.env.MIA_NARRATIVE_PIPER_PATH}`);
16
+ return process.env.MIA_NARRATIVE_PIPER_PATH;
17
+ }
18
+ // 2. Try to find piper in PATH
19
+ try {
20
+ const result = execSync('which piper', { stdio: 'pipe' }).toString().trim();
21
+ if (result) {
22
+ Logger.debug(`Found piper in PATH: ${result}`);
23
+ return result;
24
+ }
25
+ }
26
+ catch {
27
+ // Not in PATH
28
+ }
29
+ // 3. Default fallback
30
+ Logger.debug('Using default piper path: piper (will try to find in PATH at runtime)');
31
+ return 'piper';
32
+ }
33
+ /**
34
+ * Get the path to piper model file
35
+ * Priority: MIA_NARRATIVE_PIPER_MODEL env var → /opt/mianar/piper-tts/... → ~/.local/share/piper-tts/...
36
+ */
37
+ export function getModelPath() {
38
+ // 1. Check explicit environment variable
39
+ if (process.env.MIA_NARRATIVE_PIPER_MODEL) {
40
+ Logger.debug(`Using MIA_NARRATIVE_PIPER_MODEL: ${process.env.MIA_NARRATIVE_PIPER_MODEL}`);
41
+ return process.env.MIA_NARRATIVE_PIPER_MODEL;
42
+ }
43
+ // 2. Check /opt/mianar structure
44
+ if (existsSync(OPT_MIANAR_PIPER_MODEL)) {
45
+ Logger.debug(`Found model in /opt/mianar: ${OPT_MIANAR_PIPER_MODEL}`);
46
+ return OPT_MIANAR_PIPER_MODEL;
47
+ }
48
+ // 3. Default to home directory structure
49
+ const defaultPath = join(homedir(), '.local/share/piper-tts');
50
+ Logger.debug(`Using default model directory: ${defaultPath}`);
51
+ return defaultPath;
52
+ }
53
+ /**
54
+ * Check piper binary availability
55
+ */
56
+ export function isPiperAvailable() {
57
+ try {
58
+ const piperPath = getPiperPath();
59
+ if (piperPath === 'piper') {
60
+ // Try to find piper in PATH
61
+ execSync('which piper > /dev/null 2>&1');
62
+ }
63
+ else if (existsSync(piperPath)) {
64
+ return true;
65
+ }
66
+ else {
67
+ return false;
68
+ }
69
+ return true;
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ }
75
+ /**
76
+ * Check if model is available
77
+ */
78
+ export function isModelAvailable(modelName = 'en_US-lessac-medium') {
79
+ const modelPath = getModelPath();
80
+ const modelFile = join(modelPath, `${modelName}.onnx`);
81
+ const configFile = `${modelFile}.json`;
82
+ return existsSync(modelFile) && existsSync(configFile);
83
+ }
84
+ /**
85
+ * Get comprehensive prerequisite status
86
+ */
87
+ export function getPrerequisiteStatus() {
88
+ const piperPath = getPiperPath();
89
+ const modelPath = getModelPath();
90
+ return {
91
+ piperPath,
92
+ piperAvailable: isPiperAvailable(),
93
+ modelPath,
94
+ modelAvailable: isModelAvailable(),
95
+ configPath: `${modelPath}/en_US-lessac-medium.onnx.json`,
96
+ configAvailable: existsSync(join(modelPath, 'en_US-lessac-medium.onnx.json')),
97
+ };
98
+ }
99
+ /**
100
+ * Generate helpful error message when piper is not available
101
+ */
102
+ export function getPiperMissingMessage() {
103
+ const status = getPrerequisiteStatus();
104
+ return `
105
+ ❌ Piper TTS is not available
106
+
107
+ Piper path checked: ${status.piperPath}
108
+ Piper available: ${status.piperAvailable ? '✓' : '✗'}
109
+
110
+ To install Piper:
111
+ 1. Using pip:
112
+ pip install -U piper-tts
113
+
114
+ 2. Then ensure it's in your PATH or set MIA_NARRATIVE_PIPER_PATH
115
+
116
+ 3. Or use the setup script:
117
+ npm run setup
118
+
119
+ For more info: https://github.com/rhasspy/piper
120
+ `;
121
+ }
122
+ /**
123
+ * Generate helpful error message when models are not available
124
+ */
125
+ export function getModelsMissingMessage() {
126
+ const status = getPrerequisiteStatus();
127
+ return `
128
+ ❌ Piper TTS models are not available
129
+
130
+ Looking in: ${status.modelPath}
131
+ Models available: ${status.modelAvailable ? '✓' : '✗'}
132
+
133
+ To download models:
134
+ 1. Run the setup script:
135
+ npm run setup
136
+
137
+ 2. Or manually download to:
138
+ ${status.modelPath}
139
+
140
+ Expected file:
141
+ en_US-lessac-medium.onnx
142
+ en_US-lessac-medium.onnx.json
143
+ `;
144
+ }
145
+ /**
146
+ * Ensure directory exists for models
147
+ */
148
+ export function ensureModelDirectory() {
149
+ const modelPath = getModelPath();
150
+ if (!existsSync(modelPath)) {
151
+ mkdirSync(modelPath, { recursive: true });
152
+ Logger.debug(`Created model directory: ${modelPath}`);
153
+ }
154
+ }
155
+ /**
156
+ * Check if mpg123 (MP3 audio player) is installed
157
+ */
158
+ export function isMpg123Available() {
159
+ try {
160
+ execSync('which mpg123 > /dev/null 2>&1');
161
+ return true;
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ /**
168
+ * Generate helpful message about mpg123 installation
169
+ */
170
+ export function getMpg123MissingMessage() {
171
+ return `
172
+ ⚠️ MP3 player (mpg123) not found
173
+
174
+ To play generated audio files, install mpg123:
175
+ sudo apt-get install mpg123 -y (Linux)
176
+ brew install mpg123 (macOS)
177
+
178
+ Or manually download from: https://www.mpg123.de/
179
+
180
+ After installation, audio playback will work automatically.
181
+ `;
182
+ }
183
+ /**
184
+ * Attempt to install mpg123 automatically (Linux only)
185
+ */
186
+ export function tryInstallMpg123() {
187
+ try {
188
+ const { platform } = require('os');
189
+ // Only try on Linux systems with apt
190
+ if (platform() !== 'linux') {
191
+ return false;
192
+ }
193
+ execSync('which apt-get > /dev/null 2>&1');
194
+ Logger.debug('Attempting to install mpg123...');
195
+ execSync('sudo apt-get install mpg123 -y', { stdio: 'pipe' });
196
+ Logger.debug('✓ mpg123 installed successfully');
197
+ return true;
198
+ }
199
+ catch (error) {
200
+ Logger.debug(`Failed to auto-install mpg123: ${error}`);
201
+ return false;
202
+ }
203
+ }
204
+ /**
205
+ * Ensure mpg123 is available, try to install if missing
206
+ */
207
+ export function ensureMpg123() {
208
+ if (isMpg123Available()) {
209
+ return true;
210
+ }
211
+ // Try to install
212
+ if (tryInstallMpg123()) {
213
+ return true;
214
+ }
215
+ Logger.warn(getMpg123MissingMessage());
216
+ return false;
217
+ }
218
+ //# sourceMappingURL=prerequisites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prerequisites.js","sourceRoot":"","sources":["../../../src/utils/prerequisites.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mia-narrative",
3
- "version": "1.0.10",
3
+ "version": "1.1.0",
4
4
  "description": "Text-to-speech CLI for narrative audio generation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,89 @@
1
+ fread() # Read a file with --file instead of --text (default: Mia voice)
2
+ {
3
+ local _file="$1"
4
+ if [ -z "$_file" ];then
5
+ echo "Usage: fread <file>"
6
+ return 1
7
+ fi
8
+ local _withou_extension=$(basename "$_file" | cut -f 1 -d '.')
9
+ #the output file is the same as the input file but .mp3 and is NOT in /tmp
10
+ local _output_file="$_withou_extension.mp3"
11
+
12
+ mia-narrative generate --engine piper --file "$_file" --output $_output_file || {
13
+ echo "Failed to generate audio with mia-narrative."
14
+ return 1
15
+ }
16
+
17
+ # Try to play audio, but don't fail if unavailable (e.g., headless/SSH without forwarding)
18
+ if mpg123 $_output_file 2>/dev/null; then
19
+ : # Audio played successfully
20
+ else
21
+ echo "✓ Generated: $_output_file (audio playback unavailable - use audio-gaia/audio-hu to enable)"
22
+ fi
23
+ }
24
+
25
+ # Persona-specific TTS functions
26
+ fread_mia() { # Professional female narrator
27
+ local _file="$1"
28
+ [ -z "$_file" ] && echo "Usage: fread_mia <file>" && return 1
29
+ local _output="$(basename "$_file" .txt).mp3"
30
+ mia-narrative generate --engine piper --voiceId mia --file "$_file" --output "$_output" || return 1
31
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
32
+ }
33
+
34
+ fread_miette() { # Conversational female narrator
35
+ local _file="$1"
36
+ [ -z "$_file" ] && echo "Usage: fread_miette <file>" && return 1
37
+ local _output="$(basename "$_file" .txt).mp3"
38
+ mia-narrative generate --engine piper --voiceId miette --file "$_file" --output "$_output" || return 1
39
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
40
+ }
41
+
42
+ fread_seraphine() { # Expressive narrative female voice
43
+ local _file="$1"
44
+ [ -z "$_file" ] && echo "Usage: fread_seraphine <file>" && return 1
45
+ local _output="$(basename "$_file" .txt).mp3"
46
+ mia-narrative generate --engine piper --voiceId seraphine --file "$_file" --output "$_output" || return 1
47
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
48
+ }
49
+
50
+ fread_jeremy() { # Professional male narrator
51
+ local _file="$1"
52
+ [ -z "$_file" ] && echo "Usage: fread_jeremy <file>" && return 1
53
+ local _output="$(basename "$_file" .txt).mp3"
54
+ mia-narrative generate --engine piper --voiceId jeremy --file "$_file" --output "$_output" || return 1
55
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
56
+ }
57
+
58
+ fread_atlas() { # Casual male voice
59
+ local _file="$1"
60
+ [ -z "$_file" ] && echo "Usage: fread_atlas <file>" && return 1
61
+ local _output="$(basename "$_file" .txt).mp3"
62
+ mia-narrative generate --engine piper --voiceId atlas --file "$_file" --output "$_output" || return 1
63
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
64
+ }
65
+
66
+ fread_resonova() { # Expressive female voice
67
+ local _file="$1"
68
+ [ -z "$_file" ] && echo "Usage: fread_resonova <file>" && return 1
69
+ local _output="$(basename "$_file" .txt).mp3"
70
+ mia-narrative generate --engine piper --voiceId resonova --file "$_file" --output "$_output" || return 1
71
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
72
+ }
73
+
74
+ fread_zephyr() { # Neutral contemplative voice
75
+ local _file="$1"
76
+ [ -z "$_file" ] && echo "Usage: fread_zephyr <file>" && return 1
77
+ local _output="$(basename "$_file" .txt).mp3"
78
+ mia-narrative generate --engine piper --voiceId zephyr --file "$_file" --output "$_output" || return 1
79
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
80
+ }
81
+
82
+ fread_echo() { # Expressive character voice
83
+ local _file="$1"
84
+ [ -z "$_file" ] && echo "Usage: fread_echo <file>" && return 1
85
+ local _output="$(basename "$_file" .txt).mp3"
86
+ mia-narrative generate --engine piper --voiceId echo --file "$_file" --output "$_output" || return 1
87
+ mpg123 "$_output" 2>/dev/null || echo "✓ Generated: $_output (audio playback unavailable)"
88
+ }
89
+
package/scripts/setup.js CHANGED
@@ -7,6 +7,8 @@ import { join } from 'path';
7
7
 
8
8
  const HOME = homedir();
9
9
  const PIPER_MODEL_DIR = join(HOME, '.local/share/piper-tts');
10
+ const OPT_MIANAR_PATH = '/opt/mianar';
11
+ const DROPBOX_URL = 'https://www.dropbox.com/scl/fi/vl4mmi4wpz0eqtjljdh6c/opt_mianar_prerequisite_piper.tar?rlkey=paq9nfeoppfzjua1whsu7476z&dl=1';
10
12
 
11
13
  // Voice models to download (maps to voice profiles)
12
14
  const MODELS = [
@@ -20,30 +22,53 @@ const MODELS = [
20
22
 
21
23
  console.log('\n🎙️ mia-narrative Setup\n');
22
24
 
25
+ // Check if /opt/mianar prerequisites are already downloaded
26
+ console.log('1️⃣ Checking for /opt/mianar prerequisites...');
27
+ if (existsSync(OPT_MIANAR_PATH)) {
28
+ const piperTtsPath = join(OPT_MIANAR_PATH, 'piper-tts');
29
+ if (existsSync(piperTtsPath)) {
30
+ console.log(' ✓ Found /opt/mianar/piper-tts');
31
+ console.log(` → Using: ${piperTtsPath}`);
32
+ process.env.MIA_NARRATIVE_PIPER_MODEL = join(piperTtsPath, 'en_US-lessac-medium.onnx');
33
+ }
34
+ } else {
35
+ console.log(' ⚠️ /opt/mianar not found, will download to home directory');
36
+ }
37
+
23
38
  // Check Piper installation
24
- console.log('1️⃣ Checking for Piper TTS...');
39
+ console.log('2️⃣ Checking for Piper TTS...');
25
40
  try {
26
41
  execSync('which piper > /dev/null 2>&1');
27
42
  console.log(' ✓ Piper is installed');
28
43
  } catch {
29
44
  console.log(' ⚠️ Piper not found in PATH');
30
- console.log(' → Install from: https://github.com/rhasspy/piper');
31
- process.exit(1);
45
+ console.log(' → Install from: pip install -U piper-tts');
46
+ console.log(' → After install, make sure "piper" is in your PATH');
32
47
  }
33
48
 
34
49
  // Check FFmpeg
35
- console.log('2️⃣ Checking for FFmpeg...');
50
+ console.log('3️⃣ Checking for FFmpeg...');
36
51
  try {
37
52
  execSync('which ffmpeg > /dev/null 2>&1');
38
53
  console.log(' ✓ FFmpeg is installed');
39
54
  } catch {
40
55
  console.log(' ⚠️ FFmpeg not found');
41
56
  console.log(' → Install: brew install ffmpeg (macOS) or apt-get install ffmpeg (Linux)');
42
- process.exit(1);
57
+ }
58
+
59
+ // Check mpg123
60
+ console.log('4️⃣ Checking for MP3 player (mpg123)...');
61
+ try {
62
+ execSync('which mpg123 > /dev/null 2>&1');
63
+ console.log(' ✓ mpg123 is installed');
64
+ } catch {
65
+ console.log(' ⚠️ mpg123 not found');
66
+ console.log(' → For MP3 playback, install: sudo apt-get install mpg123 -y (Linux)');
67
+ console.log(' → Or: brew install mpg123 (macOS)');
43
68
  }
44
69
 
45
70
  // Check/Setup Piper models
46
- console.log(`3️⃣ Setting up Piper voice models...`);
71
+ console.log(`5️⃣ Setting up Piper voice models...`);
47
72
  mkdirSync(PIPER_MODEL_DIR, { recursive: true });
48
73
 
49
74
  const totalSize = MODELS.reduce((sum, m) => {
@@ -97,10 +122,18 @@ for (const model of MODELS) {
97
122
  console.log('');
98
123
  if (downloadedCount === 0) {
99
124
  console.log(' ⚠️ No models downloaded. Check your internet connection.');
100
- process.exit(1);
125
+ } else {
126
+ console.log(` ✓ Downloaded ${downloadedCount}/${MODELS.length} models`);
101
127
  }
102
128
 
103
- console.log(` ✓ Downloaded ${downloadedCount}/${MODELS.length} models`);
129
+ // Suggest /opt/mianar download
130
+ console.log('\n6️⃣ Optional: Download /opt/mianar prerequisites...');
131
+ if (!existsSync(OPT_MIANAR_PATH)) {
132
+ console.log(' → To use /opt/mianar structure:');
133
+ console.log(' → Run: mkdir -p /opt/mianar && sudo chown $USER /opt/mianar');
134
+ console.log(' → Then: wget -q -O - ' + DROPBOX_URL + ' | tar -xz -C /opt/mianar');
135
+ console.log(' → Set: export MIA_NARRATIVE_PIPER_MODEL=/opt/mianar/piper-tts/en_US-lessac-medium.onnx');
136
+ }
104
137
 
105
138
  console.log('\n✅ Setup complete!');
106
139
  console.log('\nModels installed to:', PIPER_MODEL_DIR);
@@ -108,4 +141,7 @@ console.log('\nNext steps:');
108
141
  console.log('1. Test: mia-narrative generate --engine piper --text "hello"');
109
142
  console.log('2. List voices: mia-narrative voices --engine piper');
110
143
  console.log('3. Try a specific voice: mia-narrative generate --engine piper --voice miette-default --text "hello"');
144
+ console.log('\nEnvironment variables you can set:');
145
+ console.log(' MIA_NARRATIVE_PIPER_PATH=/path/to/piper');
146
+ console.log(' MIA_NARRATIVE_PIPER_MODEL=/path/to/model.onnx or /path/to/models/directory');
111
147
  console.log('\n');
@@ -6,23 +6,23 @@ import { randomUUID } from 'crypto';
6
6
  import { Logger } from '../utils/logger.js';
7
7
  import { TTSEngine, TTSEngineConfig, GenerateAudioOptions, Voice } from './base.js';
8
8
  import { getVoiceProfile, VOICE_PROFILES } from '../config/voices.js';
9
+ import { getPiperPath, getModelPath, getPiperMissingMessage } from '../utils/prerequisites.js';
9
10
 
10
11
  export class PiperEngine extends TTSEngine {
11
12
  private piperPath: string = 'piper';
12
13
  private modelPath: string = '';
13
14
 
14
15
  async initialize(config: TTSEngineConfig): Promise<void> {
15
- if (config.piperPath) {
16
- this.piperPath = config.piperPath;
17
- }
16
+ // Use provided config or fall back to environment/auto-detection
17
+ this.piperPath = config.piperPath || getPiperPath();
18
18
 
19
19
  if (config.modelPath) {
20
20
  this.modelPath = config.modelPath;
21
21
  } else {
22
- this.modelPath = process.env.MIA_NARRATIVE_PIPER_MODEL || '';
22
+ this.modelPath = getModelPath();
23
23
  }
24
24
 
25
- Logger.debug(`Piper engine initialized with model path: ${this.modelPath}`);
25
+ Logger.debug(`Piper engine initialized with piper path: ${this.piperPath}, model path: ${this.modelPath}`);
26
26
  }
27
27
 
28
28
  private preprocessText(text: string): string {
@@ -43,15 +43,21 @@ export class PiperEngine extends TTSEngine {
43
43
  throw new Error('Text cannot be empty');
44
44
  }
45
45
 
46
- // Build path to model file
47
- const modelDir = join(homedir(), '.local/share/piper-tts');
48
- const modelFile = join(modelDir, `${voiceProfile.piperModel}.onnx`);
46
+ // Build path to model file using configured model path
47
+ let modelFile: string;
48
+ if (this.modelPath.endsWith('.onnx')) {
49
+ // If modelPath is already a full model path, use it directly
50
+ modelFile = this.modelPath;
51
+ } else {
52
+ // Otherwise, treat it as a directory and append model filename
53
+ modelFile = join(this.modelPath, `${voiceProfile.piperModel}.onnx`);
54
+ }
49
55
 
50
56
  if (!existsSync(modelFile)) {
51
57
  throw new Error(
52
58
  `Piper model not found: ${voiceProfile.piperModel}\n` +
53
59
  `Expected location: ${modelFile}\n` +
54
- `Run 'npm run setup' to download models.`
60
+ `Run 'npm run setup' to download models or set MIA_NARRATIVE_PIPER_MODEL environment variable.`
55
61
  );
56
62
  }
57
63
 
@@ -83,7 +89,9 @@ export class PiperEngine extends TTSEngine {
83
89
  }
84
90
 
85
91
  async getVoices(): Promise<Voice[]> {
86
- const modelDir = join(homedir(), '.local/share/piper-tts');
92
+ const modelDir = this.modelPath.endsWith('.onnx')
93
+ ? this.modelPath.substring(0, this.modelPath.lastIndexOf('/'))
94
+ : this.modelPath;
87
95
 
88
96
  return Object.values(VOICE_PROFILES)
89
97
  .filter(v => {
@@ -102,7 +110,6 @@ export class PiperEngine extends TTSEngine {
102
110
  if (this.piperPath === 'piper') {
103
111
  execSync('which piper', { stdio: 'pipe' });
104
112
  } else {
105
- const { existsSync } = await import('fs');
106
113
  if (!existsSync(this.piperPath)) {
107
114
  return false;
108
115
  }
@@ -155,7 +162,7 @@ export class PiperEngine extends TTSEngine {
155
162
  piper.on('error', (error) => {
156
163
  reject(
157
164
  new Error(
158
- `Failed to start Piper: ${error.message}. Ensure Piper is installed and in your PATH.`
165
+ `Failed to start Piper: ${error.message}\n\n${getPiperMissingMessage()}`
159
166
  )
160
167
  );
161
168
  });
@@ -25,10 +25,22 @@ export class FileReader {
25
25
 
26
26
  /**
27
27
  * Strip Markdown syntax from text, leaving only readable content
28
+ * Also adds strategic pauses for better TTS pacing
28
29
  */
29
30
  static stripMarkdown(text: string): string {
30
31
  let result = text;
31
32
 
33
+ // Add pauses before headers (before stripping syntax)
34
+ // Insert period+newline twice before headers for longer pause
35
+ result = result.replace(/^(#{1,6})\s+/gm, '\n\n. \n. \n$1 ');
36
+
37
+ // Add pause after list items (before stripping list markers)
38
+ result = result.replace(/^([\s]*[-*+]\s+.+)$/gm, '$1. ');
39
+ result = result.replace(/^([\s]*\d+\.\s+.+)$/gm, '$1. ');
40
+
41
+ // Add pause after colons (in regular text)
42
+ result = result.replace(/:\s+([^\n])/g, ': . $1');
43
+
32
44
  // Remove code blocks (``` ... ```)
33
45
  result = result.replace(/```[\s\S]*?```/g, '');
34
46
 
@@ -0,0 +1,245 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, mkdirSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { join } from 'path';
5
+ import { Logger } from './logger.js';
6
+
7
+ const OPT_MIANAR_PATH = '/opt/mianar';
8
+ const OPT_MIANAR_PIPER_MODEL = '/opt/mianar/piper-tts/en_US-lessac-medium.onnx';
9
+
10
+ export interface PrerequisiteStatus {
11
+ piperPath: string;
12
+ piperAvailable: boolean;
13
+ modelPath: string;
14
+ modelAvailable: boolean;
15
+ configPath: string;
16
+ configAvailable: boolean;
17
+ }
18
+
19
+ /**
20
+ * Get the path to the piper binary
21
+ * Priority: MIA_NARRATIVE_PIPER_PATH env var → which piper → /opt/mianar/bin/piper
22
+ */
23
+ export function getPiperPath(): string {
24
+ // 1. Check explicit environment variable
25
+ if (process.env.MIA_NARRATIVE_PIPER_PATH) {
26
+ Logger.debug(`Using MIA_NARRATIVE_PIPER_PATH: ${process.env.MIA_NARRATIVE_PIPER_PATH}`);
27
+ return process.env.MIA_NARRATIVE_PIPER_PATH;
28
+ }
29
+
30
+ // 2. Try to find piper in PATH
31
+ try {
32
+ const result = execSync('which piper', { stdio: 'pipe' }).toString().trim();
33
+ if (result) {
34
+ Logger.debug(`Found piper in PATH: ${result}`);
35
+ return result;
36
+ }
37
+ } catch {
38
+ // Not in PATH
39
+ }
40
+
41
+ // 3. Default fallback
42
+ Logger.debug('Using default piper path: piper (will try to find in PATH at runtime)');
43
+ return 'piper';
44
+ }
45
+
46
+ /**
47
+ * Get the path to piper model file
48
+ * Priority: MIA_NARRATIVE_PIPER_MODEL env var → /opt/mianar/piper-tts/... → ~/.local/share/piper-tts/...
49
+ */
50
+ export function getModelPath(): string {
51
+ // 1. Check explicit environment variable
52
+ if (process.env.MIA_NARRATIVE_PIPER_MODEL) {
53
+ Logger.debug(`Using MIA_NARRATIVE_PIPER_MODEL: ${process.env.MIA_NARRATIVE_PIPER_MODEL}`);
54
+ return process.env.MIA_NARRATIVE_PIPER_MODEL;
55
+ }
56
+
57
+ // 2. Check /opt/mianar structure
58
+ if (existsSync(OPT_MIANAR_PIPER_MODEL)) {
59
+ Logger.debug(`Found model in /opt/mianar: ${OPT_MIANAR_PIPER_MODEL}`);
60
+ return OPT_MIANAR_PIPER_MODEL;
61
+ }
62
+
63
+ // 3. Default to home directory structure
64
+ const defaultPath = join(homedir(), '.local/share/piper-tts');
65
+ Logger.debug(`Using default model directory: ${defaultPath}`);
66
+ return defaultPath;
67
+ }
68
+
69
+ /**
70
+ * Check piper binary availability
71
+ */
72
+ export function isPiperAvailable(): boolean {
73
+ try {
74
+ const piperPath = getPiperPath();
75
+ if (piperPath === 'piper') {
76
+ // Try to find piper in PATH
77
+ execSync('which piper > /dev/null 2>&1');
78
+ } else if (existsSync(piperPath)) {
79
+ return true;
80
+ } else {
81
+ return false;
82
+ }
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Check if model is available
91
+ */
92
+ export function isModelAvailable(modelName: string = 'en_US-lessac-medium'): boolean {
93
+ const modelPath = getModelPath();
94
+ const modelFile = join(modelPath, `${modelName}.onnx`);
95
+ const configFile = `${modelFile}.json`;
96
+
97
+ return existsSync(modelFile) && existsSync(configFile);
98
+ }
99
+
100
+ /**
101
+ * Get comprehensive prerequisite status
102
+ */
103
+ export function getPrerequisiteStatus(): PrerequisiteStatus {
104
+ const piperPath = getPiperPath();
105
+ const modelPath = getModelPath();
106
+
107
+ return {
108
+ piperPath,
109
+ piperAvailable: isPiperAvailable(),
110
+ modelPath,
111
+ modelAvailable: isModelAvailable(),
112
+ configPath: `${modelPath}/en_US-lessac-medium.onnx.json`,
113
+ configAvailable: existsSync(join(modelPath, 'en_US-lessac-medium.onnx.json')),
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Generate helpful error message when piper is not available
119
+ */
120
+ export function getPiperMissingMessage(): string {
121
+ const status = getPrerequisiteStatus();
122
+
123
+ return `
124
+ ❌ Piper TTS is not available
125
+
126
+ Piper path checked: ${status.piperPath}
127
+ Piper available: ${status.piperAvailable ? '✓' : '✗'}
128
+
129
+ To install Piper:
130
+ 1. Using pip:
131
+ pip install -U piper-tts
132
+
133
+ 2. Then ensure it's in your PATH or set MIA_NARRATIVE_PIPER_PATH
134
+
135
+ 3. Or use the setup script:
136
+ npm run setup
137
+
138
+ For more info: https://github.com/rhasspy/piper
139
+ `;
140
+ }
141
+
142
+ /**
143
+ * Generate helpful error message when models are not available
144
+ */
145
+ export function getModelsMissingMessage(): string {
146
+ const status = getPrerequisiteStatus();
147
+
148
+ return `
149
+ ❌ Piper TTS models are not available
150
+
151
+ Looking in: ${status.modelPath}
152
+ Models available: ${status.modelAvailable ? '✓' : '✗'}
153
+
154
+ To download models:
155
+ 1. Run the setup script:
156
+ npm run setup
157
+
158
+ 2. Or manually download to:
159
+ ${status.modelPath}
160
+
161
+ Expected file:
162
+ en_US-lessac-medium.onnx
163
+ en_US-lessac-medium.onnx.json
164
+ `;
165
+ }
166
+
167
+ /**
168
+ * Ensure directory exists for models
169
+ */
170
+ export function ensureModelDirectory(): void {
171
+ const modelPath = getModelPath();
172
+ if (!existsSync(modelPath)) {
173
+ mkdirSync(modelPath, { recursive: true });
174
+ Logger.debug(`Created model directory: ${modelPath}`);
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Check if mpg123 (MP3 audio player) is installed
180
+ */
181
+ export function isMpg123Available(): boolean {
182
+ try {
183
+ execSync('which mpg123 > /dev/null 2>&1');
184
+ return true;
185
+ } catch {
186
+ return false;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Generate helpful message about mpg123 installation
192
+ */
193
+ export function getMpg123MissingMessage(): string {
194
+ return `
195
+ ⚠️ MP3 player (mpg123) not found
196
+
197
+ To play generated audio files, install mpg123:
198
+ sudo apt-get install mpg123 -y (Linux)
199
+ brew install mpg123 (macOS)
200
+
201
+ Or manually download from: https://www.mpg123.de/
202
+
203
+ After installation, audio playback will work automatically.
204
+ `;
205
+ }
206
+
207
+ /**
208
+ * Attempt to install mpg123 automatically (Linux only)
209
+ */
210
+ export function tryInstallMpg123(): boolean {
211
+ try {
212
+ const { platform } = require('os');
213
+ // Only try on Linux systems with apt
214
+ if (platform() !== 'linux') {
215
+ return false;
216
+ }
217
+
218
+ execSync('which apt-get > /dev/null 2>&1');
219
+
220
+ Logger.debug('Attempting to install mpg123...');
221
+ execSync('sudo apt-get install mpg123 -y', { stdio: 'pipe' });
222
+ Logger.debug('✓ mpg123 installed successfully');
223
+ return true;
224
+ } catch (error) {
225
+ Logger.debug(`Failed to auto-install mpg123: ${error}`);
226
+ return false;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Ensure mpg123 is available, try to install if missing
232
+ */
233
+ export function ensureMpg123(): boolean {
234
+ if (isMpg123Available()) {
235
+ return true;
236
+ }
237
+
238
+ // Try to install
239
+ if (tryInstallMpg123()) {
240
+ return true;
241
+ }
242
+
243
+ Logger.warn(getMpg123MissingMessage());
244
+ return false;
245
+ }