agentvibes 5.1.4 → 5.2.1

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.
Files changed (69) hide show
  1. package/.agentvibes/config.json +23 -13
  2. package/.claude/commands/agent-vibes/verbosity.md +98 -89
  3. package/.claude/config/audio-effects.cfg +4 -1
  4. package/.claude/hooks/audio-cache-utils.sh +246 -246
  5. package/.claude/hooks/background-music-manager.sh +404 -404
  6. package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
  7. package/.claude/hooks/bmad-speak.sh +290 -290
  8. package/.claude/hooks/bmad-tts-injector.sh +568 -568
  9. package/.claude/hooks/bmad-voice-manager.sh +928 -928
  10. package/.claude/hooks/clawdbot-receiver-SECURE.sh +129 -129
  11. package/.claude/hooks/clawdbot-receiver.sh +107 -107
  12. package/.claude/hooks/clean-audio-cache.sh +22 -22
  13. package/.claude/hooks/cleanup-cache.sh +106 -106
  14. package/.claude/hooks/configure-rdp-mode.sh +137 -137
  15. package/.claude/hooks/download-extra-voices.sh +244 -244
  16. package/.claude/hooks/effects-manager.sh +268 -268
  17. package/.claude/hooks/github-star-reminder.sh +154 -154
  18. package/.claude/hooks/language-manager.sh +362 -362
  19. package/.claude/hooks/learn-manager.sh +492 -492
  20. package/.claude/hooks/macos-voice-manager.sh +205 -205
  21. package/.claude/hooks/migrate-background-music.sh +125 -125
  22. package/.claude/hooks/migrate-to-agentvibes.sh +161 -161
  23. package/.claude/hooks/optimize-background-music.sh +87 -87
  24. package/.claude/hooks/path-resolver.sh +60 -60
  25. package/.claude/hooks/personality-manager.sh +448 -448
  26. package/.claude/hooks/piper-download-voices.sh +233 -225
  27. package/.claude/hooks/piper-installer.sh +292 -292
  28. package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
  29. package/.claude/hooks/piper-voice-manager.sh +125 -0
  30. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +97 -90
  31. package/.claude/hooks/play-tts-enhanced.sh +105 -105
  32. package/.claude/hooks/play-tts-piper.sh +16 -5
  33. package/.claude/hooks/play-tts-ssh-remote.sh +168 -167
  34. package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
  35. package/.claude/hooks/play-tts.sh +35 -14
  36. package/.claude/hooks/prepare-release.sh +54 -54
  37. package/.claude/hooks/provider-commands.sh +617 -617
  38. package/.claude/hooks/provider-manager.sh +399 -399
  39. package/.claude/hooks/replay-target-audio.sh +95 -95
  40. package/.claude/hooks/sentiment-manager.sh +201 -201
  41. package/.claude/hooks/session-start-tts.sh +4 -1
  42. package/.claude/hooks/speed-manager.sh +291 -291
  43. package/.claude/hooks/stop-tts.sh +84 -84
  44. package/.claude/hooks/termux-installer.sh +261 -261
  45. package/.claude/hooks/translate-manager.sh +341 -341
  46. package/.claude/hooks/tts-queue-worker.sh +145 -145
  47. package/.claude/hooks/tts-queue.sh +165 -165
  48. package/.claude/hooks/verbosity-manager.sh +185 -178
  49. package/.claude/hooks/voice-manager.sh +552 -548
  50. package/.claude/hooks-windows/download-extra-voices.ps1 +243 -185
  51. package/.claude/hooks-windows/play-tts-piper.ps1 +7 -2
  52. package/.claude/hooks-windows/play-tts.ps1 +9 -3
  53. package/.claude/hooks-windows/session-start-tts.ps1 +2 -1
  54. package/.claude/hooks-windows/verbosity-manager.ps1 +126 -119
  55. package/README.md +19 -2
  56. package/RELEASE_NOTES.md +74 -0
  57. package/bin/agentvibes-voice-browser.js +1939 -1840
  58. package/bin/mcp-server.sh +206 -206
  59. package/mcp-server/server.py +87 -15
  60. package/package.json +1 -1
  61. package/src/console/tabs/receiver-tab.js +1527 -1483
  62. package/src/console/tabs/settings-tab.js +2 -2
  63. package/src/console/tabs/setup-tab.js +112 -31
  64. package/src/console/tabs/voices-tab.js +130 -13
  65. package/src/i18n/en.js +202 -202
  66. package/src/installer.js +79 -213
  67. package/src/services/llm-provider-service.js +126 -75
  68. package/src/services/verbosity-service.js +159 -157
  69. package/templates/agentvibes-receiver.sh +3 -2
@@ -1,171 +1,171 @@
1
- #!/usr/bin/env bash
2
- #
3
- # File: .claude/hooks/piper-multispeaker-registry.sh
4
- #
5
- # AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
6
- # Website: https://agentvibes.org
7
- # Repository: https://github.com/paulpreibisch/AgentVibes
8
- #
9
- # Co-created by Paul Preibisch with Claude AI
10
- # Copyright (c) 2025 Paul Preibisch
11
- #
12
- # Licensed under the Apache License, Version 2.0 (the "License");
13
- # you may not use this file except in compliance with the License.
14
- # You may obtain a copy of the License at
15
- #
16
- # http://www.apache.org/licenses/LICENSE-2.0
17
- #
18
- # Unless required by applicable law or agreed to in writing, software
19
- # distributed under the License is distributed on an "AS IS" BASIS,
20
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
- # See the License for the specific language governing permissions and
22
- # limitations under the License.
23
- #
24
- # DISCLAIMER: This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
- # express or implied, including but not limited to the warranties of
26
- # merchantability, fitness for a particular purpose and noninfringement.
27
- # In no event shall the authors or copyright holders be liable for any claim,
28
- # damages or other liability, whether in an action of contract, tort or
29
- # otherwise, arising from, out of or in connection with the software or the
30
- # use or other dealings in the software.
31
- #
32
- # ---
33
- #
34
- # @fileoverview Multi-Speaker Voice Registry - Maps speaker names to ONNX models and speaker IDs
35
- # @context Enables individual speaker selection from multi-speaker Piper models (e.g., 16Speakers)
36
- # @architecture Static registry mapping speaker names to model files and numeric speaker IDs
37
- # @dependencies piper-voice-manager.sh (voice storage), play-tts-piper.sh (TTS with speaker ID)
38
- # @entrypoints Sourced by voice-manager.sh for multi-speaker voice switching
39
- # @patterns Registry pattern, speaker ID mapping, model-to-speaker association
40
- # @related voice-manager.sh, play-tts-piper.sh, 16Speakers.onnx.json (speaker_id_map)
41
- #
42
-
43
- # Bash 3.2 compatible lowercase function (macOS ships with bash 3.2)
44
- # ${var,,} syntax requires bash 4.0+
45
- _to_lower() {
46
- echo "$1" | tr '[:upper:]' '[:lower:]'
47
- }
48
-
49
- # Registry of multi-speaker models and their speaker names
50
- # Format: "SpeakerName:model_file:speaker_id:description"
51
- #
52
- # 16Speakers Model (12 US + 4 UK voices):
53
- # Source: LibriVox Public Domain recordings
54
- # Model: 16Speakers.onnx (77MB)
55
- #
56
- MULTISPEAKER_VOICES=(
57
- # US English Speakers (0-11)
58
- "Cori_Samuel:16Speakers:0:US English Female"
59
- "Kara_Shallenberg:16Speakers:1:US English Female"
60
- "Kristin_Hughes:16Speakers:2:US English Female"
61
- "Maria_Kasper:16Speakers:3:US English Female"
62
- "Mike_Pelton:16Speakers:4:US English Male"
63
- "Mark_Nelson:16Speakers:5:US English Male"
64
- "Michael_Scherer:16Speakers:6:US English Male"
65
- "James_K_White:16Speakers:7:US English Male"
66
- "Rose_Ibex:16Speakers:8:US English Female"
67
- "progressingamerica:16Speakers:9:US English Male"
68
- "Steve_C:16Speakers:10:US English Male"
69
- "Owlivia:16Speakers:11:US English Female"
70
-
71
- # UK English Speakers (12-15)
72
- "Paul_Hampton:16Speakers:12:UK English Male"
73
- "Jennifer_Dorr:16Speakers:13:UK English Female"
74
- "Emily_Cripps:16Speakers:14:UK English Female"
75
- "Martin_Clifton:16Speakers:15:UK English Male"
76
- )
77
-
78
- # @function get_multispeaker_info
79
- # @intent Get model and speaker ID for a speaker name
80
- # @why Enables users to select individual speakers from multi-speaker models by name
81
- # @param $1 {string} speaker_name - Speaker name (e.g., "Cori_Samuel", "Rose_Ibex")
82
- # @returns Echoes "model:speaker_id" (e.g., "16Speakers:0") to stdout
83
- # @exitcode 0=speaker found, 1=speaker not found
84
- # @sideeffects None (read-only lookup)
85
- # @edgecases Case-insensitive matching
86
- # @calledby voice-manager.sh switch command
87
- # @calls None (pure bash array iteration)
88
- get_multispeaker_info() {
89
- local speaker_name="$1"
90
- for entry in "${MULTISPEAKER_VOICES[@]}"; do
91
- name="${entry%%:*}"
92
- rest="${entry#*:}"
93
- model="${rest%%:*}"
94
- rest="${rest#*:}"
95
- speaker_id="${rest%%:*}"
96
-
97
- if [[ "$(_to_lower "$name")" == "$(_to_lower "$speaker_name")" ]]; then
98
- echo "$model:$speaker_id"
99
- return 0
100
- fi
101
- done
102
- return 1
103
- }
104
-
105
- # @function list_multispeaker_voices
106
- # @intent Display all available multi-speaker voices with descriptions
107
- # @why Help users discover individual speakers within multi-speaker models
108
- # @param None
109
- # @returns None
110
- # @exitcode Always 0
111
- # @sideeffects Writes formatted list to stdout
112
- # @edgecases None
113
- # @calledby voice-manager.sh list command, /agent-vibes:list
114
- # @calls None (pure bash array iteration)
115
- list_multispeaker_voices() {
116
- echo "🎭 Multi-Speaker Voices (16Speakers Model):"
117
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
118
-
119
- local current_model=""
120
- for entry in "${MULTISPEAKER_VOICES[@]}"; do
121
- name="${entry%%:*}"
122
- rest="${entry#*:}"
123
- model="${rest%%:*}"
124
- rest="${rest#*:}"
125
- speaker_id="${rest%%:*}"
126
- description="${rest#*:}"
127
-
128
- # Print section header when model changes
129
- if [[ "$model" != "$current_model" ]]; then
130
- if [[ -n "$current_model" ]]; then
131
- echo ""
132
- fi
133
- echo " Model: $model.onnx"
134
- current_model="$model"
135
- fi
136
-
137
- echo " • $name (ID: $speaker_id) - $description"
138
- done
139
-
140
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
141
- echo ""
142
- echo "Usage: /agent-vibes:switch Cori_Samuel"
143
- echo " /agent-vibes:switch Rose_Ibex"
144
- }
145
-
146
- # @function get_multispeaker_description
147
- # @intent Get description for a speaker name
148
- # @why Provide user-friendly info about speaker characteristics
149
- # @param $1 {string} speaker_name - Speaker name
150
- # @returns Echoes description (e.g., "US English Female") to stdout
151
- # @exitcode 0=speaker found, 1=speaker not found
152
- # @sideeffects None (read-only lookup)
153
- # @edgecases Case-insensitive matching
154
- # @calledby voice-manager.sh switch command (for confirmation message)
155
- # @calls None (pure bash array iteration)
156
- get_multispeaker_description() {
157
- local speaker_name="$1"
158
- for entry in "${MULTISPEAKER_VOICES[@]}"; do
159
- name="${entry%%:*}"
160
- rest="${entry#*:}"
161
- rest="${rest#*:}"
162
- rest="${rest#*:}"
163
- description="${rest}"
164
-
165
- if [[ "$(_to_lower "$name")" == "$(_to_lower "$speaker_name")" ]]; then
166
- echo "$description"
167
- return 0
168
- fi
169
- done
170
- return 1
171
- }
1
+ #!/usr/bin/env bash
2
+ #
3
+ # File: .claude/hooks/piper-multispeaker-registry.sh
4
+ #
5
+ # AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
6
+ # Website: https://agentvibes.org
7
+ # Repository: https://github.com/paulpreibisch/AgentVibes
8
+ #
9
+ # Co-created by Paul Preibisch with Claude AI
10
+ # Copyright (c) 2025 Paul Preibisch
11
+ #
12
+ # Licensed under the Apache License, Version 2.0 (the "License");
13
+ # you may not use this file except in compliance with the License.
14
+ # You may obtain a copy of the License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the License is distributed on an "AS IS" BASIS,
20
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ # See the License for the specific language governing permissions and
22
+ # limitations under the License.
23
+ #
24
+ # DISCLAIMER: This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
+ # express or implied, including but not limited to the warranties of
26
+ # merchantability, fitness for a particular purpose and noninfringement.
27
+ # In no event shall the authors or copyright holders be liable for any claim,
28
+ # damages or other liability, whether in an action of contract, tort or
29
+ # otherwise, arising from, out of or in connection with the software or the
30
+ # use or other dealings in the software.
31
+ #
32
+ # ---
33
+ #
34
+ # @fileoverview Multi-Speaker Voice Registry - Maps speaker names to ONNX models and speaker IDs
35
+ # @context Enables individual speaker selection from multi-speaker Piper models (e.g., 16Speakers)
36
+ # @architecture Static registry mapping speaker names to model files and numeric speaker IDs
37
+ # @dependencies piper-voice-manager.sh (voice storage), play-tts-piper.sh (TTS with speaker ID)
38
+ # @entrypoints Sourced by voice-manager.sh for multi-speaker voice switching
39
+ # @patterns Registry pattern, speaker ID mapping, model-to-speaker association
40
+ # @related voice-manager.sh, play-tts-piper.sh, 16Speakers.onnx.json (speaker_id_map)
41
+ #
42
+
43
+ # Bash 3.2 compatible lowercase function (macOS ships with bash 3.2)
44
+ # ${var,,} syntax requires bash 4.0+
45
+ _to_lower() {
46
+ echo "$1" | tr '[:upper:]' '[:lower:]'
47
+ }
48
+
49
+ # Registry of multi-speaker models and their speaker names
50
+ # Format: "SpeakerName:model_file:speaker_id:description"
51
+ #
52
+ # 16Speakers Model (12 US + 4 UK voices):
53
+ # Source: LibriVox Public Domain recordings
54
+ # Model: 16Speakers.onnx (77MB)
55
+ #
56
+ MULTISPEAKER_VOICES=(
57
+ # US English Speakers (0-11)
58
+ "Cori_Samuel:16Speakers:0:US English Female"
59
+ "Kara_Shallenberg:16Speakers:1:US English Female"
60
+ "Kristin_Hughes:16Speakers:2:US English Female"
61
+ "Maria_Kasper:16Speakers:3:US English Female"
62
+ "Mike_Pelton:16Speakers:4:US English Male"
63
+ "Mark_Nelson:16Speakers:5:US English Male"
64
+ "Michael_Scherer:16Speakers:6:US English Male"
65
+ "James_K_White:16Speakers:7:US English Male"
66
+ "Rose_Ibex:16Speakers:8:US English Female"
67
+ "progressingamerica:16Speakers:9:US English Male"
68
+ "Steve_C:16Speakers:10:US English Male"
69
+ "Owlivia:16Speakers:11:US English Female"
70
+
71
+ # UK English Speakers (12-15)
72
+ "Paul_Hampton:16Speakers:12:UK English Male"
73
+ "Jennifer_Dorr:16Speakers:13:UK English Female"
74
+ "Emily_Cripps:16Speakers:14:UK English Female"
75
+ "Martin_Clifton:16Speakers:15:UK English Male"
76
+ )
77
+
78
+ # @function get_multispeaker_info
79
+ # @intent Get model and speaker ID for a speaker name
80
+ # @why Enables users to select individual speakers from multi-speaker models by name
81
+ # @param $1 {string} speaker_name - Speaker name (e.g., "Cori_Samuel", "Rose_Ibex")
82
+ # @returns Echoes "model:speaker_id" (e.g., "16Speakers:0") to stdout
83
+ # @exitcode 0=speaker found, 1=speaker not found
84
+ # @sideeffects None (read-only lookup)
85
+ # @edgecases Case-insensitive matching
86
+ # @calledby voice-manager.sh switch command
87
+ # @calls None (pure bash array iteration)
88
+ get_multispeaker_info() {
89
+ local speaker_name="$1"
90
+ for entry in "${MULTISPEAKER_VOICES[@]}"; do
91
+ name="${entry%%:*}"
92
+ rest="${entry#*:}"
93
+ model="${rest%%:*}"
94
+ rest="${rest#*:}"
95
+ speaker_id="${rest%%:*}"
96
+
97
+ if [[ "$(_to_lower "$name")" == "$(_to_lower "$speaker_name")" ]]; then
98
+ echo "$model:$speaker_id"
99
+ return 0
100
+ fi
101
+ done
102
+ return 1
103
+ }
104
+
105
+ # @function list_multispeaker_voices
106
+ # @intent Display all available multi-speaker voices with descriptions
107
+ # @why Help users discover individual speakers within multi-speaker models
108
+ # @param None
109
+ # @returns None
110
+ # @exitcode Always 0
111
+ # @sideeffects Writes formatted list to stdout
112
+ # @edgecases None
113
+ # @calledby voice-manager.sh list command, /agent-vibes:list
114
+ # @calls None (pure bash array iteration)
115
+ list_multispeaker_voices() {
116
+ echo "🎭 Multi-Speaker Voices (16Speakers Model):"
117
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
118
+
119
+ local current_model=""
120
+ for entry in "${MULTISPEAKER_VOICES[@]}"; do
121
+ name="${entry%%:*}"
122
+ rest="${entry#*:}"
123
+ model="${rest%%:*}"
124
+ rest="${rest#*:}"
125
+ speaker_id="${rest%%:*}"
126
+ description="${rest#*:}"
127
+
128
+ # Print section header when model changes
129
+ if [[ "$model" != "$current_model" ]]; then
130
+ if [[ -n "$current_model" ]]; then
131
+ echo ""
132
+ fi
133
+ echo " Model: $model.onnx"
134
+ current_model="$model"
135
+ fi
136
+
137
+ echo " • $name (ID: $speaker_id) - $description"
138
+ done
139
+
140
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
141
+ echo ""
142
+ echo "Usage: /agent-vibes:switch Cori_Samuel"
143
+ echo " /agent-vibes:switch Rose_Ibex"
144
+ }
145
+
146
+ # @function get_multispeaker_description
147
+ # @intent Get description for a speaker name
148
+ # @why Provide user-friendly info about speaker characteristics
149
+ # @param $1 {string} speaker_name - Speaker name
150
+ # @returns Echoes description (e.g., "US English Female") to stdout
151
+ # @exitcode 0=speaker found, 1=speaker not found
152
+ # @sideeffects None (read-only lookup)
153
+ # @edgecases Case-insensitive matching
154
+ # @calledby voice-manager.sh switch command (for confirmation message)
155
+ # @calls None (pure bash array iteration)
156
+ get_multispeaker_description() {
157
+ local speaker_name="$1"
158
+ for entry in "${MULTISPEAKER_VOICES[@]}"; do
159
+ name="${entry%%:*}"
160
+ rest="${entry#*:}"
161
+ rest="${rest#*:}"
162
+ rest="${rest#*:}"
163
+ description="${rest}"
164
+
165
+ if [[ "$(_to_lower "$name")" == "$(_to_lower "$speaker_name")" ]]; then
166
+ echo "$description"
167
+ return 0
168
+ fi
169
+ done
170
+ return 1
171
+ }
@@ -265,10 +265,135 @@ download_voice() {
265
265
  return 1
266
266
  fi
267
267
 
268
+ # Patch LibriTTS speaker names if this is a libritts model
269
+ if [[ "$voice_name" == *libritts* ]]; then
270
+ patch_libritts_speaker_names "$voice_dir" "$voice_name"
271
+ fi
272
+
268
273
  echo "✅ Voice downloaded successfully: $voice_name"
269
274
  echo " Location: $voice_dir/${voice_name}.onnx"
270
275
  }
271
276
 
277
+ # @function patch_libritts_speaker_names
278
+ # @intent Replace raw corpus IDs (p3922, p8699) with friendly names (Anna, Bella) in LibriTTS .onnx.json
279
+ # @why Users see cryptic "p100Bell" names instead of friendly names without this patch
280
+ # @param $1 {string} voice_dir - Directory containing the .onnx.json file
281
+ # @param $2 {string} voice_name - Voice model name (e.g., en_US-libritts-high)
282
+ # @returns None
283
+ # @exitcode Always 0 (non-fatal)
284
+ # @sideeffects Rewrites speaker_id_map in .onnx.json with friendly names from voice-assignments.json
285
+ # @calledby download_voice (for libritts models), piper-download-voices.sh (post-download)
286
+ # @calls python3/node (for JSON manipulation)
287
+ patch_libritts_speaker_names() {
288
+ local voice_dir="$1"
289
+ local voice_name="$2"
290
+ local json_file="$voice_dir/${voice_name}.onnx.json"
291
+
292
+ if [[ ! -f "$json_file" ]]; then
293
+ return 0
294
+ fi
295
+
296
+ # Find voice-assignments.json relative to this script (SCRIPT_DIR/../.. = project root)
297
+ local script_dir
298
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
299
+ local project_root
300
+ project_root="$(cd "$script_dir/../.." 2>/dev/null && pwd)"
301
+ local catalog="$project_root/voice-assignments.json"
302
+
303
+ # Also check npm global install location
304
+ if [[ ! -f "$catalog" ]]; then
305
+ # Try npm root
306
+ local npm_root
307
+ npm_root="$(npm root -g 2>/dev/null)/agentvibes" || true
308
+ if [[ -f "$npm_root/voice-assignments.json" ]]; then
309
+ catalog="$npm_root/voice-assignments.json"
310
+ fi
311
+ fi
312
+
313
+ if [[ ! -f "$catalog" ]]; then
314
+ return 0
315
+ fi
316
+
317
+ # Check if already patched (first key doesn't start with 'p' + digits)
318
+ if command -v python3 &>/dev/null; then
319
+ python3 -c "
320
+ import json, sys
321
+
322
+ json_path = sys.argv[1]
323
+ catalog_path = sys.argv[2]
324
+
325
+ with open(json_path, 'r') as f:
326
+ data = json.load(f)
327
+
328
+ sid_map = data.get('speaker_id_map', {})
329
+ if not sid_map or data.get('num_speakers', 0) <= 1:
330
+ sys.exit(0)
331
+
332
+ # Check if already patched
333
+ import re
334
+ first_key = next(iter(sid_map))
335
+ if not re.match(r'^p\d+$', first_key):
336
+ sys.exit(0)
337
+
338
+ # Load catalog
339
+ with open(catalog_path, 'r') as f:
340
+ catalog = json.load(f)
341
+
342
+ speakers = catalog.get('libritts_speakers', {})
343
+
344
+ # Build reverse map: index -> p-name
345
+ index_to_p = {v: k for k, v in sid_map.items()}
346
+
347
+ # Rebuild with friendly names
348
+ new_map = {}
349
+ for idx, pname in index_to_p.items():
350
+ friendly = speakers.get(str(idx), {}).get('voice_name')
351
+ new_map[friendly if friendly else pname] = idx
352
+
353
+ data['speaker_id_map'] = new_map
354
+
355
+ with open(json_path, 'w') as f:
356
+ json.dump(data, f, indent=2)
357
+
358
+ print(' Patched LibriTTS speaker names to friendly names')
359
+ " "$json_file" "$catalog" 2>/dev/null || true
360
+ elif command -v node &>/dev/null; then
361
+ node -e "
362
+ const fs = require('fs');
363
+ const path = require('path');
364
+
365
+ const jsonPath = process.argv[1];
366
+ const catalogPath = process.argv[2];
367
+
368
+ const data = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
369
+ const sidMap = data.speaker_id_map || {};
370
+ if (!Object.keys(sidMap).length || (data.num_speakers || 0) <= 1) process.exit(0);
371
+
372
+ // Check if already patched
373
+ const firstKey = Object.keys(sidMap)[0];
374
+ if (!/^p\d+$/.test(firstKey)) process.exit(0);
375
+
376
+ const catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
377
+ const speakers = catalog.libritts_speakers || {};
378
+
379
+ // Build reverse map
380
+ const indexToP = {};
381
+ for (const [pname, idx] of Object.entries(sidMap)) indexToP[idx] = pname;
382
+
383
+ // Rebuild with friendly names
384
+ const newMap = {};
385
+ for (const [idx, pname] of Object.entries(indexToP)) {
386
+ const friendly = speakers[String(idx)]?.voice_name;
387
+ newMap[friendly || pname] = parseInt(idx, 10);
388
+ }
389
+
390
+ data.speaker_id_map = newMap;
391
+ fs.writeFileSync(jsonPath, JSON.stringify(data, null, 2), 'utf8');
392
+ console.log(' Patched LibriTTS speaker names to friendly names');
393
+ " "$json_file" "$catalog" 2>/dev/null || true
394
+ fi
395
+ }
396
+
272
397
  # @function list_downloaded_voices
273
398
  # @intent Display all locally cached voice models with file sizes
274
399
  # @why Help users see what voices they have available and storage usage