feat(audio): add intelligent multi-lingual romanization tool (Japanese & Korean -> Latin) to voice asset uploader
This commit is contained in:
+2
-1
@@ -1,7 +1,7 @@
|
||||
from . import (
|
||||
audio_catalog, folder_types, gallery_routes, materials, media_routes, model_catalog,
|
||||
model_media, model_metadata, model_resolution, recipe_packages, recipes,
|
||||
translation_routes,
|
||||
romanizer, translation_routes,
|
||||
)
|
||||
from .scanner import *
|
||||
from .config import *
|
||||
@@ -105,3 +105,4 @@ def setup_routes(app):
|
||||
app.router.add_post('/anomalous/delete_audio_gallery', audio_catalog.api_delete_audio_gallery)
|
||||
app.router.add_get('/anomalous/audio_template_workflow', audio_catalog.api_get_audio_template_workflow)
|
||||
app.router.add_post('/anomalous/upload_audio_voice', audio_catalog.api_upload_audio_voice)
|
||||
app.router.add_post('/anomalous/romanize_text', romanizer.api_romanize_text)
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
"""Intelligent Romanization Engine for Japanese and Korean text alignment in F5-TTS."""
|
||||
|
||||
import re
|
||||
import json
|
||||
from aiohttp import web
|
||||
|
||||
_kakasi_instance = None
|
||||
_hangul_transliter = None
|
||||
|
||||
|
||||
def _get_kakasi():
|
||||
global _kakasi_instance
|
||||
if _kakasi_instance is None:
|
||||
try:
|
||||
import pykakasi
|
||||
_kakasi_instance = pykakasi.kakasi()
|
||||
except Exception:
|
||||
_kakasi_instance = False
|
||||
return _kakasi_instance if _kakasi_instance else None
|
||||
|
||||
|
||||
def _get_hangul_transliter():
|
||||
global _hangul_transliter
|
||||
if _hangul_transliter is None:
|
||||
try:
|
||||
from hangul_romanize import Transliter
|
||||
from hangul_romanize.rule import academic
|
||||
_hangul_transliter = Transliter(academic)
|
||||
except Exception:
|
||||
_hangul_transliter = False
|
||||
return _hangul_transliter if _hangul_transliter else None
|
||||
|
||||
|
||||
def _clean_romanized_spacing(text: str) -> str:
|
||||
"""Normalize whitespace and punctuation spacing for speech synthesis alignment."""
|
||||
# Remove hyphens often generated in academic Korean romanization between syllables
|
||||
text = re.sub(r'(\w)-(\w)', r'\1\2', text)
|
||||
# Fix spaces preceding punctuation: 'word ,' -> 'word,'
|
||||
text = re.sub(r'\s+([,.:;!?~])', r'\1', text)
|
||||
# Ensure a space follows punctuation if directly adjacent to a letter: 'word,next' -> 'word, next'
|
||||
text = re.sub(r'([,.:;!?])([A-Za-z0-9])', r'\1 \2', text)
|
||||
# Collapse multiple whitespaces
|
||||
text = re.sub(r'[ \t]+', ' ', text)
|
||||
return text.strip()
|
||||
|
||||
|
||||
def romanize_text(text: str) -> dict:
|
||||
"""
|
||||
Detect language and convert Japanese or Korean into standard Latin/Romaji phonetics.
|
||||
Returns dict with original, romanized, detected language, and success status.
|
||||
"""
|
||||
raw = (text or "").strip()
|
||||
if not raw:
|
||||
return {"success": True, "original": "", "romanized": "", "lang": "empty"}
|
||||
|
||||
has_hangul = bool(re.search(r'[\uac00-\ud7a3]', raw))
|
||||
has_kana = bool(re.search(r'[\u3040-\u30ff]', raw))
|
||||
|
||||
# 1. Process Korean text
|
||||
if has_hangul:
|
||||
ht = _get_hangul_transliter()
|
||||
if ht:
|
||||
try:
|
||||
romanized = ht.translit(raw)
|
||||
return {
|
||||
"success": True,
|
||||
"original": raw,
|
||||
"romanized": _clean_romanized_spacing(romanized),
|
||||
"lang": "ko"
|
||||
}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Korean transliteration failed: {e}", "original": raw}
|
||||
|
||||
# 2. Process Japanese text (Kana or Japanese context)
|
||||
if has_kana or (not has_hangul and bool(re.search(r'[\u4e00-\u9fff]', raw))):
|
||||
kakasi = _get_kakasi()
|
||||
if kakasi:
|
||||
try:
|
||||
result = kakasi.convert(raw)
|
||||
parts = []
|
||||
for item in result:
|
||||
hep = item.get("hepburn", "")
|
||||
parts.append(hep if hep else item.get("orig", ""))
|
||||
romanized = " ".join(parts)
|
||||
return {
|
||||
"success": True,
|
||||
"original": raw,
|
||||
"romanized": _clean_romanized_spacing(romanized),
|
||||
"lang": "ja"
|
||||
}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Japanese romanization failed: {e}", "original": raw}
|
||||
|
||||
# Default: Already Latin or other language, keep as is
|
||||
return {
|
||||
"success": True,
|
||||
"original": raw,
|
||||
"romanized": raw,
|
||||
"lang": "unchanged"
|
||||
}
|
||||
|
||||
|
||||
async def api_romanize_text(request):
|
||||
"""POST /anomalous/romanize_text - Convert input text to Romaji / Latin transliteration."""
|
||||
try:
|
||||
data = await request.json()
|
||||
except Exception:
|
||||
return web.json_response({"success": False, "error": "Invalid JSON body"}, status=400)
|
||||
|
||||
text = data.get("text", "")
|
||||
res = romanize_text(text)
|
||||
status_code = 200 if res.get("success") else 500
|
||||
return web.json_response(res, status=status_code)
|
||||
Reference in New Issue
Block a user