mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Read Aloud config: Show already downloaded voices in bold
This commit is contained in:
parent
065bafd0c5
commit
4ae5fe59c0
@ -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()
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user