Skip to content

fix(chat): azure tts and gifs - #17

Merged
Linaryx merged 4 commits into
ruinateam:mainfrom
Addefan:fix/azure-tts-and-gifs
Nov 8, 2026
Merged

Linaryx merged 4 commits into
ruinateam:mainfrom
Addefan:fix/azure-tts-and-gifs

Conversation

@Addefan

@Addefan Addefan commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Azure TTS

Screenshot from 2026-10-29 01-45-33 Screenshot from 2026-10-29 00-49-37

The https://chatsemban.justdavi.dev/api/tts API endpoint stopped working (returning HTTP 500), which broke the Dmitry and Svetlana voices in the !tts command.

I deployed a compatible API endpoint with Azure voices on my own server and updated the endpoint URL in the code. I don't plan on taking it down in the foreseeable future, but in case it ever needs to be self-hosted elsewhere, here is the script to deploy the exact same endpoint:

# server.py

from fastapi import FastAPI, Query, Response
from fastapi.middleware.cors import CORSMiddleware
import edge_tts

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/api/tts")
async def tts(text: str = Query(...), voice: str = Query("ru-RU-DmitryNeural")):
    try:
        communicate = edge_tts.Communicate(text, voice)
        audio_data = bytearray()
        async for chunk in communicate.stream():
            if chunk["type"] == "audio":
                audio_data.extend(chunk["data"])
        return Response(content=bytes(audio_data), media_type="audio/mpeg")
    except Exception as e:
        return Response(content=str(e), status_code=500)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

Required dependencies: edge-tts, fastapi, and uvicorn (pip install edge-tts fastapi uvicorn).
Running python server.py starts the server at http://0.0.0.0:8000. HTTPS and a custom domain can be configured using NGINX + Let's Encrypt.

GIFs

image

All Twitch GIF messages have a multi-word fallback text format like [<title> GIF by <author>]. GIF rendering was already implemented in the project, but the unit tests only checked a single-word [GIF] string. Because the message text is tokenized by whitespace before rendering (createMessageTokenSnapshot), the full multi-word GIF substring stored in replacements never matched the individual word tokens during lookup and ended up being rendered as plain text.

Before tokenizing the message, I now replace the GIF substring with a whitespace-free placeholder so it stays a single token and properly matches the entry in replacements.

VoiceCatalog.tsx

Screenshot from 2026-10-29 01-39-01

Prior to any of these changes, tests/voiceCatalog.test.ts was failing in the original repository because it imported a .tsx file directly (outside Vite, Bun's built-in JSX transpiler attempts to import jsxDEV from solid-js, which does not exist at runtime).

I extracted the pure parsing logic from src/components/setup/VoiceCatalog.tsx into src/components/setup/voiceCatalogData.ts so the test imports a plain .ts module without JSX.

@Linaryx

Linaryx commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator

@Addefan Thanks 🤙

@Linaryx
Linaryx merged commit d5f84c1 into ruinateam:main Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants