fix(voices): archetype language filter accepts ISO codes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5dbe8c8998
commit
8fed5eeda8
@ -10,6 +10,7 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
- **Voice list rows no longer show the "Source unknown" badge** (it crowded the play buttons). The badge stays in the voice inspector, next to the Source & consent panel.
|
||||
|
||||
### Fixed
|
||||
- **Archetype `language` filter accepted only full names.** `GET /api/archetypes?language=de` returned 0 items; it now accepts ISO codes and names, case-insensitive (`de`, `DE`, `German`); unknown values return an empty list.
|
||||
- **LibriVox search returned 502 "unavailable" after 20-30 s.** Cause: the LibriVox API is erratic (10-40 s for any request, measured from host and container; egress/DNS/TLS fine) and the old call asked for `extended=1` (all chapters of 15 books) with a 30 s timeout, mapping every failure to one generic message. Now the list is light (no `extended`), the first chapter is fetched per book on demand (`GET /api/voice-sources/librivox/book/{id}`, UI "Load preview" / on import), timeout 25 s with one retry and backoff, failures cached 30 s, distinct messages (timeout / unreachable / HTTP status / unreadable) and a privacy-safe log line (exception class and query length only). Host/redirect rules unchanged.
|
||||
- **`export_provenance_default` missing from settings defaults.** It is now in the defaults (`true`), so `GET /api/settings` exposes it.
|
||||
### Added
|
||||
|
||||
@ -5,6 +5,8 @@ DESIGN_CHIPS taxonomy of static/js/voice-clone.js. Validated at import.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from core.open_voice_sources import LANG_NAMES
|
||||
|
||||
FACET_VOCAB = {
|
||||
"gender": {"male", "female", "neutral"},
|
||||
"age": {"child", "teenager", "young adult", "middle-aged", "elderly"},
|
||||
@ -147,15 +149,22 @@ BY_ID = {a["id"]: a for a in ARCHETYPES}
|
||||
MAX_LIMIT = 100
|
||||
|
||||
|
||||
def _lang_name(v: str) -> str:
|
||||
"""ISO code or full name, case-insensitive -> canonical name; unknown values pass through (match nothing)."""
|
||||
v = (v or "").strip()
|
||||
return LANG_NAMES.get(v.lower()) or next((n for n in LANG_NAMES.values() if n.lower() == v.lower()), v)
|
||||
|
||||
|
||||
def list_archetypes(gender="", age="", pitch="", accent="", language="", category="", q="",
|
||||
offset=0, limit=24) -> dict:
|
||||
flt = {"gender": gender, "age": age, "pitch": pitch, "accent": accent}
|
||||
ql = (q or "").strip().lower()[:100]
|
||||
lang = _lang_name(language)
|
||||
rows = []
|
||||
for a in ARCHETYPES:
|
||||
if any(v and a["facets"][k] != v for k, v in flt.items()):
|
||||
continue
|
||||
if language and a["language"] != language or category and a["category"] != category:
|
||||
if lang and a["language"] != lang or category and a["category"] != category:
|
||||
continue
|
||||
if ql and ql not in f'{a["name_de"]} {a["name_en"]} {a["instruct"]} {a["category"]}'.lower():
|
||||
continue
|
||||
|
||||
@ -51,6 +51,19 @@ class Catalog(unittest.TestCase):
|
||||
self.assertNotEqual(va.preview_key(a, "m"), va.preview_key(a, "m", 7))
|
||||
|
||||
|
||||
def test_language_filter_codes_and_names(self):
|
||||
n = lambda l: va.list_archetypes(language=l)["total"]
|
||||
de = n("German")
|
||||
self.assertGreater(de, 0)
|
||||
self.assertEqual(n("de"), de)
|
||||
self.assertEqual(n("DE"), de)
|
||||
self.assertEqual(n("german"), de)
|
||||
self.assertEqual(n("en"), n("English"))
|
||||
self.assertGreater(n("en"), 0)
|
||||
self.assertEqual(n("xx"), 0)
|
||||
self.assertEqual(n("French"), 0)
|
||||
|
||||
|
||||
class UseRoute(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory(); self.addCleanup(self.tmp.cleanup)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user