Read Aloud config: Show already downloaded voices in bold

This commit is contained in:
Kovid Goyal 2024-09-28 11:51:00 +05:30
parent 065bafd0c5
commit 4ae5fe59c0
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 30 additions and 3 deletions

View File

@ -3,6 +3,7 @@
from qt.core import (
QCheckBox,
QFont,
QFormLayout,
QHBoxLayout,
QIcon,
@ -136,20 +137,30 @@ class Voices(QTreeWidget):
self.setHeaderHidden(True)
self.system_default_voice = Voice()
self.currentItemChanged.connect(self.voice_changed)
self.normal_font = f = self.font()
self.highlight_font = f = QFont(f)
f.setBold(True), f.setItalic(True)
def sizeHint(self) -> QSize:
return QSize(400, 500)
def set_item_downloaded_state(self, ans: QTreeWidgetItem) -> None:
voice = ans.data(0, Qt.ItemDataRole.UserRole)
is_downloaded = bool(voice.engine_data and voice.engine_data.get('is_downloaded'))
ans.setFont(0, self.highlight_font if is_downloaded else self.normal_font)
def set_voices(self, all_voices: tuple[Voice, ...], current_voice: str, engine_metadata: EngineMetadata) -> None:
self.clear()
current_item = None
def qv(parent, voice):
nonlocal current_item
ans = QTreeWidgetItem(parent, [voice.short_text(engine_metadata)])
text = voice.short_text(engine_metadata)
ans = QTreeWidgetItem(parent, [text])
ans.setData(0, Qt.ItemDataRole.UserRole, voice)
ans.setToolTip(0, voice.tooltip(engine_metadata))
if current_voice == voice.name:
current_item = ans
self.set_item_downloaded_state(ans)
return ans
qv(self.invisibleRootItem(), self.system_default_voice)
vmap = {}
@ -182,6 +193,11 @@ class Voices(QTreeWidget):
if ci is not None:
return ci.data(0, Qt.ItemDataRole.UserRole)
def refresh_current_item(self) -> None:
ci = self.currentItem()
if ci is not None:
self.set_item_downloaded_state(ci)
class EngineSpecificConfig(QWidget):
@ -357,6 +373,7 @@ class ConfigDialog(Dialog):
else:
b.setIcon(QIcon.ic('download-metadata.png'))
b.setText(_('Download voice'))
self.engine_specific_config.voices.refresh_current_item()
def voice_action(self):
self.engine_specific_config.voice_action()

View File

@ -406,6 +406,9 @@ class Piper(TTSBackend):
ans = []
lang_voices_map = {}
self._voice_name_map = {}
downloaded = set()
with suppress(OSError):
downloaded = set(os.listdir(self.cache_dir))
for bcp_code, voice_map in d['lang_map'].items():
lang, sep, country = bcp_code.partition('_')
lang = canonicalize_lang(lang) or lang
@ -416,9 +419,10 @@ class Piper(TTSBackend):
q = Quality.from_piper_quality(qual)
if best_qual is None or q.value < best_qual.value:
best_qual = q
mf = f'{bcp_code}-{voice_name}-{qual}.onnx'
voice = Voice(bcp_code + ':' + voice_name, lang, country, human_name=voice_name, quality=q, engine_data={
'model_url': e['model'], 'config_url': e['config'],
'model_filename': f'{bcp_code}-{voice_name}-{qual}.onnx',
'model_filename': mf, 'is_downloaded': mf in downloaded,
})
if voice:
ans.append(voice)
@ -442,9 +446,13 @@ class Piper(TTSBackend):
lang = canonicalize_lang(lang) or lang
return self._voice_for_lang.get(lang) or self._voice_for_lang['eng']
@property
def cache_dir(self) -> str:
return os.path.join(cache_dir(), 'piper-voices')
def _paths_for_voice(self, voice: Voice) -> tuple[str, str]:
fname = voice.engine_data['model_filename']
model_path = os.path.join(cache_dir(), 'piper-voices', fname)
model_path = os.path.join(self.cache_dir, fname)
config_path = os.path.join(os.path.dirname(model_path), fname + '.json')
return model_path, config_path
@ -462,6 +470,7 @@ class Piper(TTSBackend):
for path in self._paths_for_voice(v):
with suppress(FileNotFoundError):
os.remove(path)
v.engine_data['is_downloaded'] = False
def _download_voice(self, voice: Voice, download_even_if_exists: bool = False) -> tuple[str, str]:
model_path, config_path = self._paths_for_voice(voice)
@ -475,6 +484,7 @@ class Piper(TTSBackend):
voice.engine_data['config_url']: (config_path, _('Neural network metadata')),
}, parent=widget_parent(self), headless=getattr(QApplication.instance(), 'headless', False)
)
voice.engine_data['is_downloaded'] = bool(ok)
return (model_path, config_path) if ok else ('', '')
def download_voice(self, v: Voice) -> None: