wyoming-piper 1.2.0__tar.gz → 1.3.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: wyoming_piper
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Wyoming Server for Piper
5
5
  Home-page: http://github.com/rhasspy/rhasspy3
6
6
  Author: Michael Hansen
@@ -19,7 +19,7 @@ data_files = [module_dir / "voices.json"]
19
19
 
20
20
  setup(
21
21
  name="wyoming_piper",
22
- version="1.2.0",
22
+ version="1.3.0",
23
23
  description="Wyoming Server for Piper",
24
24
  url="http://github.com/rhasspy/rhasspy3",
25
25
  author="Michael Hansen",
@@ -59,13 +59,19 @@ async def main() -> None:
59
59
  help="Maximum number of piper process to run simultaneously (default: 1)",
60
60
  )
61
61
  #
62
+ parser.add_argument(
63
+ "--update-voices",
64
+ action="store_true",
65
+ help="Download latest voices.json during startup",
66
+ )
67
+ #
62
68
  parser.add_argument("--debug", action="store_true", help="Log DEBUG messages")
63
69
  args = parser.parse_args()
64
70
 
65
71
  logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
66
72
 
67
73
  # Load voice info
68
- voices_info = get_voices()
74
+ voices_info = get_voices(args.download_dir, update_voices=args.update_voices)
69
75
 
70
76
  # Resolve aliases for backwards compatibility with old voice names
71
77
  aliases_info: Dict[str, Any] = {}
@@ -102,7 +108,9 @@ async def main() -> None:
102
108
  # if voice_info.get("speaker_id_map")
103
109
  # else None,
104
110
  )
105
- for voice_name, voice_info in voices_info.items()
111
+ for voice_name, voice_info in sorted(
112
+ voices_info.items(), key=lambda kv: kv[0]
113
+ )
106
114
  if not voice_info.get("_is_alias", False)
107
115
  ],
108
116
  )
@@ -20,9 +20,28 @@ class VoiceNotFoundError(Exception):
20
20
  pass
21
21
 
22
22
 
23
- def get_voices() -> Dict[str, Any]:
24
- """Loads available voices from embedded JSON file."""
25
- with open(_DIR / "voices.json", "r", encoding="utf-8") as voices_file:
23
+ def get_voices(
24
+ download_dir: Union[str, Path], update_voices: bool = False
25
+ ) -> Dict[str, Any]:
26
+ """Loads available voices from downloaded or embedded JSON file."""
27
+ download_dir = Path(download_dir)
28
+ voices_download = download_dir / "voices.json"
29
+
30
+ if update_voices:
31
+ # Download latest voices.json
32
+ voices_url = URL_FORMAT.format(file="voices.json")
33
+ _LOGGER.debug("Downloading %s to %s", voices_url, voices_download)
34
+ with urlopen(voices_url) as response, open(
35
+ voices_download, "wb"
36
+ ) as download_file:
37
+ shutil.copyfileobj(response, download_file)
38
+
39
+ # Prefer downloaded file to embedded
40
+ voices_embedded = _DIR / "voices.json"
41
+ voices_path = voices_download if voices_download.exists() else voices_embedded
42
+
43
+ _LOGGER.debug("Loading %s", voices_path)
44
+ with open(voices_path, "r", encoding="utf-8") as voices_file:
26
45
  return json.load(voices_file)
27
46
 
28
47
 
@@ -32,10 +51,21 @@ def ensure_voice_exists(
32
51
  download_dir: Union[str, Path],
33
52
  voices_info: Dict[str, Any],
34
53
  ):
35
- assert data_dirs, "No data dirs"
36
54
  if name not in voices_info:
55
+ # Try as file path to a custom voice
56
+ onnx_path = Path(name)
57
+ config_path = Path(name + ".json")
58
+ if onnx_path.exists():
59
+ if config_path.exists():
60
+ # Custom voice found
61
+ return
62
+
63
+ _LOGGER.warning("Missing custom voice config: %s", config_path)
64
+
37
65
  raise VoiceNotFoundError(name)
38
66
 
67
+ assert data_dirs, "No data dirs"
68
+
39
69
  voice_info = voices_info[name]
40
70
  voice_files = voice_info["files"]
41
71
  files_to_download: Set[str] = set()
@@ -109,6 +139,10 @@ def ensure_voice_exists(
109
139
 
110
140
 
111
141
  def find_voice(name: str, data_dirs: Iterable[Union[str, Path]]) -> Tuple[Path, Path]:
142
+ """Looks for the files for a voice.
143
+
144
+ Returns: tuple of onnx path, config path
145
+ """
112
146
  for data_dir in data_dirs:
113
147
  data_dir = Path(data_dir)
114
148
  onnx_path = data_dir / f"{name}.onnx"
@@ -117,4 +151,11 @@ def find_voice(name: str, data_dirs: Iterable[Union[str, Path]]) -> Tuple[Path,
117
151
  if onnx_path.exists() and config_path.exists():
118
152
  return onnx_path, config_path
119
153
 
154
+ # Try as a custom voice
155
+ onnx_path = Path(name)
156
+ config_path = Path(name + ".json")
157
+
158
+ if onnx_path.exists() and config_path.exists():
159
+ return onnx_path, config_path
160
+
120
161
  raise ValueError(f"Missing files for voice {name}")
@@ -133,7 +133,6 @@ class PiperProcessManager:
133
133
  "--json-input", # piper 1.1+
134
134
  ]
135
135
 
136
- _LOGGER.debug(voice_speaker)
137
136
  if voice_speaker is not None:
138
137
  if _is_multispeaker(config):
139
138
  speaker_id = _get_speaker_id(config, voice_speaker)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: wyoming-piper
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Wyoming Server for Piper
5
5
  Home-page: http://github.com/rhasspy/rhasspy3
6
6
  Author: Michael Hansen
File without changes
File without changes