Ajay Cursor commited on
Commit ·
c14dbb6
1
Parent(s): 6b5524d
English translation mode, ms-precision time range + duration UI, drop Fly from docs
Browse files- DEPLOY.md +1 -10
- backend/main.py +2 -2
- backend/transcribe.py +2 -1
- frontend/index.html +22 -3
DEPLOY.md
CHANGED
|
@@ -38,16 +38,7 @@ Free CPU Spaces give 16 GB RAM and a stable public URL like
|
|
| 38 |
4. Add env var `SARVAM_API_KEY`. Render injects `PORT`; the container already
|
| 39 |
honors it. Deploy and share the `*.onrender.com` URL.
|
| 40 |
|
| 41 |
-
## Option C —
|
| 42 |
-
|
| 43 |
-
```bash
|
| 44 |
-
fly launch --no-deploy # generates fly.toml from the Dockerfile
|
| 45 |
-
fly secrets set SARVAM_API_KEY=your_key
|
| 46 |
-
fly scale memory 2048 # give it 2 GB
|
| 47 |
-
fly deploy
|
| 48 |
-
```
|
| 49 |
-
|
| 50 |
-
## Option D — Instant link, zero deploy (temporary)
|
| 51 |
|
| 52 |
Fastest way to show a client *right now*, while your server runs locally:
|
| 53 |
|
|
|
|
| 38 |
4. Add env var `SARVAM_API_KEY`. Render injects `PORT`; the container already
|
| 39 |
honors it. Deploy and share the `*.onrender.com` URL.
|
| 40 |
|
| 41 |
+
## Option C — Instant link, zero deploy (temporary)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
Fastest way to show a client *right now*, while your server runs locally:
|
| 44 |
|
backend/main.py
CHANGED
|
@@ -102,8 +102,8 @@ async def transcribe(
|
|
| 102 |
"speakers": speakers,
|
| 103 |
"segments": [
|
| 104 |
{
|
| 105 |
-
"start": round(s.start,
|
| 106 |
-
"end": round(s.end,
|
| 107 |
"speaker_id": s.speaker_id,
|
| 108 |
"text": s.text,
|
| 109 |
"gender": speakers.get(s.speaker_id, {}).get("label", "uncertain"),
|
|
|
|
| 102 |
"speakers": speakers,
|
| 103 |
"segments": [
|
| 104 |
{
|
| 105 |
+
"start": round(s.start, 3),
|
| 106 |
+
"end": round(s.end, 3),
|
| 107 |
"speaker_id": s.speaker_id,
|
| 108 |
"text": s.text,
|
| 109 |
"gender": speakers.get(s.speaker_id, {}).get("label", "uncertain"),
|
backend/transcribe.py
CHANGED
|
@@ -36,7 +36,8 @@ def transcribe_with_diarization(
|
|
| 36 |
|
| 37 |
job = client.speech_to_text_job.create_job(
|
| 38 |
model="saaras:v3",
|
| 39 |
-
|
|
|
|
| 40 |
language_code="unknown",
|
| 41 |
with_diarization=True,
|
| 42 |
num_speakers=num_speakers,
|
|
|
|
| 36 |
|
| 37 |
job = client.speech_to_text_job.create_job(
|
| 38 |
model="saaras:v3",
|
| 39 |
+
# "translate" makes Saaras output English regardless of the spoken language.
|
| 40 |
+
mode="translate",
|
| 41 |
language_code="unknown",
|
| 42 |
with_diarization=True,
|
| 43 |
num_speakers=num_speakers,
|
frontend/index.html
CHANGED
|
@@ -156,7 +156,7 @@
|
|
| 156 |
|
| 157 |
.transcript { display: flex; flex-direction: column; gap: 10px; }
|
| 158 |
.seg {
|
| 159 |
-
display: grid; grid-template-columns:
|
| 160 |
border-radius: 16px; border: 1px solid var(--stroke); cursor: pointer; transition: .18s cubic-bezier(.2,.8,.2,1);
|
| 161 |
background: var(--glass-2); overflow: hidden;
|
| 162 |
}
|
|
@@ -164,7 +164,14 @@
|
|
| 164 |
.seg:hover { border-color: var(--stroke-strong); transform: translateX(2px); }
|
| 165 |
.seg.active { border-color: rgba(168,85,247,.5); background: rgba(168,85,247,.09); box-shadow: 0 8px 24px rgba(168,85,247,.18); }
|
| 166 |
.seg.active::before { background: var(--grad); }
|
| 167 |
-
.seg .time { font-variant-numeric: tabular-nums;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
.seg .who { display: flex; align-items: center; gap: 9px; margin-bottom: 6px; }
|
| 169 |
.seg .who b { font-family: "Space Grotesk"; font-size: 12.5px; }
|
| 170 |
.seg .txt { font-size: 15px; line-height: 1.55; color: #e9eaf4; }
|
|
@@ -241,6 +248,13 @@
|
|
| 241 |
const sec = Math.floor(s % 60);
|
| 242 |
return `${m}:${sec.toString().padStart(2, "0")}`;
|
| 243 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
|
| 245 |
drop.addEventListener("click", () => fileInput.click());
|
| 246 |
["dragover", "dragenter"].forEach(e =>
|
|
@@ -326,8 +340,13 @@
|
|
| 326 |
el.className = "seg";
|
| 327 |
el.dataset.start = seg.start;
|
| 328 |
el.dataset.end = seg.end;
|
|
|
|
| 329 |
el.innerHTML = `
|
| 330 |
-
<div class="time"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
<div class="body">
|
| 332 |
<div class="who">
|
| 333 |
<span class="dot" style="background:${c}; box-shadow:0 0 10px ${c}88"></span>
|
|
|
|
| 156 |
|
| 157 |
.transcript { display: flex; flex-direction: column; gap: 10px; }
|
| 158 |
.seg {
|
| 159 |
+
display: grid; grid-template-columns: 96px 1fr; gap: 16px; padding: 15px 18px; position: relative;
|
| 160 |
border-radius: 16px; border: 1px solid var(--stroke); cursor: pointer; transition: .18s cubic-bezier(.2,.8,.2,1);
|
| 161 |
background: var(--glass-2); overflow: hidden;
|
| 162 |
}
|
|
|
|
| 164 |
.seg:hover { border-color: var(--stroke-strong); transform: translateX(2px); }
|
| 165 |
.seg.active { border-color: rgba(168,85,247,.5); background: rgba(168,85,247,.09); box-shadow: 0 8px 24px rgba(168,85,247,.18); }
|
| 166 |
.seg.active::before { background: var(--grad); }
|
| 167 |
+
.seg .time { font-variant-numeric: tabular-nums; padding-top: 2px;
|
| 168 |
+
display: flex; flex-direction: column; align-items: flex-start; gap: 3px; line-height: 1.25; }
|
| 169 |
+
.seg .time .t-start { color: var(--text); font-weight: 700; font-size: 12.5px; letter-spacing: .01em; transition: color .18s; }
|
| 170 |
+
.seg .time .t-end { color: var(--muted); font-size: 11px; display: flex; align-items: center; gap: 4px; }
|
| 171 |
+
.seg .time .t-end::before { content: "→"; opacity: .45; font-size: 10px; }
|
| 172 |
+
.seg .time .t-dur { margin-top: 3px; font-size: 10px; font-weight: 600; color: var(--cyan);
|
| 173 |
+
background: rgba(34,211,238,.1); border: 1px solid rgba(34,211,238,.22); padding: 1.5px 8px; border-radius: 999px; }
|
| 174 |
+
.seg.active .time .t-start { color: var(--violet); }
|
| 175 |
.seg .who { display: flex; align-items: center; gap: 9px; margin-bottom: 6px; }
|
| 176 |
.seg .who b { font-family: "Space Grotesk"; font-size: 12.5px; }
|
| 177 |
.seg .txt { font-size: 15px; line-height: 1.55; color: #e9eaf4; }
|
|
|
|
| 248 |
const sec = Math.floor(s % 60);
|
| 249 |
return `${m}:${sec.toString().padStart(2, "0")}`;
|
| 250 |
};
|
| 251 |
+
// m:ss.mmm — millisecond precision (the resolution Sarvam actually provides).
|
| 252 |
+
const fmtPrecise = (s) => {
|
| 253 |
+
const m = Math.floor(s / 60);
|
| 254 |
+
const sec = Math.floor(s % 60);
|
| 255 |
+
const ms = Math.round((s - Math.floor(s)) * 1000);
|
| 256 |
+
return `${m}:${sec.toString().padStart(2, "0")}.${ms.toString().padStart(3, "0")}`;
|
| 257 |
+
};
|
| 258 |
|
| 259 |
drop.addEventListener("click", () => fileInput.click());
|
| 260 |
["dragover", "dragenter"].forEach(e =>
|
|
|
|
| 340 |
el.className = "seg";
|
| 341 |
el.dataset.start = seg.start;
|
| 342 |
el.dataset.end = seg.end;
|
| 343 |
+
const dur = Math.max(0, seg.end - seg.start);
|
| 344 |
el.innerHTML = `
|
| 345 |
+
<div class="time" title="${seg.start}s – ${seg.end}s (${dur.toFixed(3)}s)">
|
| 346 |
+
<span class="t-start">${fmtPrecise(seg.start)}</span>
|
| 347 |
+
<span class="t-end">${fmtPrecise(seg.end)}</span>
|
| 348 |
+
<span class="t-dur">${dur.toFixed(2)}s</span>
|
| 349 |
+
</div>
|
| 350 |
<div class="body">
|
| 351 |
<div class="who">
|
| 352 |
<span class="dot" style="background:${c}; box-shadow:0 0 10px ${c}88"></span>
|