Files
UmbraAudio/umbraaudio/web/templates/_library_speakers.html
B.D. Murphy a172610b0e Add voice library, embedding-mode generation, and webUI improvements
- Add cross-project voice/name library (library.py + webUI library tabs)
- chapter_builder: embedding-mode generation + auto-load TTS base model in preflight
- qwen_tts: vmatrix capture, embedding/voice-prompt refinements
- registry: vmatrix_path column + migration for approved voices
- webUI: speakers list, chapter audio detail, proper names updates
- docs: update voice generation and tts_server README
2026-09-02 17:33:15 -07:00

72 lines
3.2 KiB
HTML

<div class="card">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem">
<h3 style="margin:0">Library Speakers ({{ speakers|length }})</h3>
<span style="color:#64748b; font-size:0.85rem">Add speakers from the project Speakers page using the "Lib" button.</span>
</div>
{% if not speakers %}
<p style="color:#64748b">No speakers in the library yet.</p>
{% else %}
<table>
<thead>
<tr>
<th>Name</th>
<th>Demographics</th>
<th>Voice</th>
<th>Source Project</th>
<th>Reference</th>
<th></th>
</tr>
</thead>
<tbody>
{% for s in speakers %}
<tr id="lib-speaker-{{ s.id }}">
<td>
<strong>{{ s.primary_name }}</strong>
{% if s.is_narrator %}<span class="badge badge-blue" style="margin-left:0.3rem">Narrator</span>{% endif %}
{% if s.is_group %}<span class="badge badge-yellow" style="margin-left:0.3rem">Group</span>{% endif %}
{% if s.aliases and s.aliases not in ('[]','') %}
<br><small style="color:#94a3b8">aliases: {{ s.aliases }}</small>
{% endif %}
</td>
<td style="font-size:0.8rem; color:#94a3b8">
{{ s.gender or '—' }}{% if s.accent %} · {{ s.accent }}{% endif %}
{% if s.vocal_traits and s.vocal_traits not in ('[]','') %}
<br><small>{{ s.vocal_traits }}</small>
{% endif %}
</td>
<td style="font-size:0.8rem; color:#94a3b8">
{% if s.distortion_effects and s.distortion_effects not in ('[]','') %}
<span style="color:#f59e0b">{{ s.distortion_effects }}</span>
{% else %}standard{% endif %}
</td>
<td style="font-size:0.8rem; color:#94a3b8">{{ s.source_project or 'manual' }}</td>
<td>
{% if s.voice_ref_path %}
{% set ref_name = s.voice_ref_path.split('/')[-1] %}
<audio controls preload="none" style="height:32px">
<source src="/library-audio/{{ s.id }}/{{ ref_name }}" type="audio/wav">
</audio>
{% else %}
<span style="color:#64748b">No ref</span>
{% endif %}
</td>
<td>
<button class="btn btn-reject" style="font-size:0.7rem; padding:0.2rem 0.5rem"
onclick="removeLibSpeaker({{ s.id }}, '{{ s.primary_name|e }}')">✕</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
<script>
function removeLibSpeaker(id, name) {
if (!confirm('Remove "' + name + '" from the library?')) return;
fetch('/api/library/speakers/' + id, {method: 'DELETE'})
.then(r => r.json()).then(d => {
htmx.ajax('GET', '/library/speakers', {target: '#lib-tab', swap: 'innerHTML'});
});
}
</script>