Skip to content

Commit 6e465b5

Browse files
authored
Merge pull request #21 from ronibhakta1/refactor/api-versionalization
refactor: update API versioning and restructure routes for cleaner organization
2 parents 06d1683 + 4109a13 commit 6e465b5

18 files changed

Lines changed: 35 additions & 35 deletions

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ make dev-docker # start server — downloads models on first run
3636

3737
Server: `http://localhost:8000` · Interactive docs: `http://localhost:8000/docs` · Demo: `http://localhost:8000/demo`
3838

39+
**Live demo:** [speech-server.readium.org/demo](https://speech-server.readium.org/demo)
40+
3941
**Quick test:**
4042
```bash
41-
curl -s -X POST http://localhost:8000/v1/synthesize \
43+
curl -s -X POST http://localhost:8000/synthesize \
4244
-H 'Content-Type: application/json' \
4345
-d '{"text":"Hello world","voice":"urn:readium:tts:pocket:en-alba"}' \
4446
-o /tmp/speech.mp3 && open /tmp/speech.mp3

‎app/api/router.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from fastapi import APIRouter
2+
3+
from app.api.routes.synthesize import router as synthesize_router
4+
from app.api.routes.voices import router as voices_router
5+
6+
router = APIRouter()
7+
router.include_router(voices_router)
8+
router.include_router(synthesize_router)

‎app/api/v1/router.py‎

Lines changed: 0 additions & 8 deletions
This file was deleted.

‎app/config/settings.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ class Settings(BaseSettings):
1515
log_level: str = "INFO"
1616
host: str = "0.0.0.0"
1717
port: int = Field(default=8000, gt=0, le=65535)
18-
api_v1_prefix: str = "/v1"
1918
workers: int = Field(default=1, ge=1)
2019
domain: str = ""
2120

‎app/main.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from starlette.middleware.trustedhost import TrustedHostMiddleware
77

88
from app.api.errors import register_error_handlers
9-
from app.api.v1.router import v1_router
10-
from app.api.v1.routes.health import router as health_router
9+
from app.api.router import router
10+
from app.api.routes.health import router as health_router
1111
from app.config.settings import settings
1212
from app.core.concurrency import init_semaphore
1313
from app.core.registry import ProviderRegistry
@@ -73,7 +73,7 @@ def create_app() -> FastAPI:
7373
register_error_handlers(app)
7474

7575
app.include_router(health_router)
76-
app.include_router(v1_router, prefix=settings.api_v1_prefix)
76+
app.include_router(router)
7777

7878
@app.get("/demo", include_in_schema=False)
7979
async def demo() -> FileResponse:

‎app/static/demo.html‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143
<body>
144144
<span class="eyebrow">TTS API Demo</span>
145145
<h1>Readium Speech Server</h1>
146-
<p>Talks to <code>GET /v1/voices</code> and <code>POST /v1/synthesize</code> on this same origin.</p>
146+
<p>Talks to <code>GET /voices</code> and <code>POST /synthesize</code> on this same origin.</p>
147147

148148
<div class="card">
149149
<label for="text">Utterance</label>
@@ -266,7 +266,7 @@ <h2>
266266
function updateCurl() {
267267
const body = JSON.stringify(requestBody());
268268
curlEl.textContent =
269-
`curl -s -X POST ${location.origin}/v1/synthesize \\\n` +
269+
`curl -s -X POST ${location.origin}/synthesize \\\n` +
270270
` -H 'Content-Type: application/json' \\\n` +
271271
` -d '${body}' \\\n` +
272272
` -o speech.${boundaryEl.checked ? 'json' : formatEl.value}`;
@@ -315,7 +315,7 @@ <h2>
315315
genderFilterEl.addEventListener('change', renderVoiceOptions);
316316

317317
async function loadVoices() {
318-
const resp = await fetch('/v1/voices');
318+
const resp = await fetch('/voices');
319319
allVoices = await resp.json();
320320
populateFilterOptions(langFilterEl, [...new Set(allVoices.map(v => v.language))].sort());
321321
const providers = [...new Set(allVoices.map(v => v.provider))].sort();
@@ -329,7 +329,7 @@ <h2>
329329
statusPillEl.style.display = 'none';
330330
outputEl.classList.remove('error');
331331
outputEl.textContent = 'Loading...';
332-
const resp = await fetch('/v1/synthesize', {
332+
const resp = await fetch('/synthesize', {
333333
method: 'POST',
334334
headers: { 'Content-Type': 'application/json' },
335335
body: JSON.stringify(requestBody()),

‎docs/API.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ Base path: `/`. All bodies are `application/json` unless noted.
1111
- [Authentication](#authentication)
1212
- [Errors](#errors)
1313
- [Health](#health)
14-
- [`GET /voices`](#get-v1voices)
15-
- [`POST /synthesize`](#post-v1synthesize)
14+
- [`GET /voices`](#get-voices)
15+
- [`POST /synthesize`](#post-synthesize)
1616
- [Not implemented](#not-implemented)
1717

1818
---
@@ -65,7 +65,7 @@ Pydantic schema errors (`422`) additionally carry an `errors` array (raw Pydanti
6565

6666
---
6767

68-
## `GET /v1/voices`
68+
## `GET /voices`
6969

7070
```
7171
GET /voices
@@ -167,7 +167,7 @@ Only `text` and `voice` are required; everything else defaults as shown.
167167
| `text` | string | — | Max `MAX_TEXT_LENGTH` chars (2000 default). Rejected if empty/whitespace after trim |
168168
| `ssml` | bool | `false` | PocketTTS strips tags before synthesis (regex `<[^>]+>` removal) — no SSML-aware prosody |
169169
| `language` | string \| null | `null` | Hint only; voice resolution is by `voiceURI`, not `language` |
170-
| `voice` | string | — | Must exactly match a `voiceURI` from `/v1/voices`. 404 if not found |
170+
| `voice` | string | — | Must exactly match a `voiceURI` from `/voices`. 404 if not found |
171171
| `prev_utterance` / `next_utterance` | string \| null | `null` | Accepted, passed into `SynthesisParams`; PocketTTS ignores both |
172172
| `publication_id` | string \| null | `null` | Accepted, currently unused (reserved for future cache scoping) |
173173
| `boundary` | bool | `false` | `true` → JSON response with base64 audio + timing marks instead of raw binary |

0 commit comments

Comments
 (0)