Retranscribe feature: ponovi STT z drugim providerjem v Edit modalu

PROBLEM: STT (Soniox/Scribe) včasih popolnoma narobe slišije besedilo —
npr. 'BILA JE LJUBEZEN PRVA' postane 'BILAL JO ME ZANPRLA' (TRIO ŠUBIC).
Edit modal je do zdaj zahteval popoln recut z novim clip range.

NEW: '🔁 Ponovi transkript' gumb v Edit modalu z dropdown za provider:
  - Scribe (ElevenLabs) — default, najboljši za nemščino + slovenščino
  - Soniox — slovenski default v auto routing
  - Whisper local (faster-whisper)

Backend:
  POST /api/jobs/{id}/retranscribe { provider, auto_upload?, lang? }
  - Briše stari MP4 + analysis.json + .srt + .ass (lokal + S3)
  - Re-queue job z whisper_provider override
  - Ohrani isti clip range — analyze.py si naredi svežo analizo
  - retranscribe_count števec za sledenje poskusov

UX: confirm dialog → 1.5s status → auto-close modal → watchJob za live progress.
Brez auto_upload v Nextcloud — preveri rezultat preden re-uploadaš.
This commit is contained in:
Claude
2026-05-03 14:38:35 +00:00
parent 2abd9daae1
commit 79f611ba73
2 changed files with 134 additions and 1 deletions
+54 -1
View File
@@ -1365,6 +1365,51 @@
refreshJobs();
}
async function retranscribeJob(id) {
const sel = document.getElementById("retranscribe-provider");
const provider = sel ? sel.value : "elevenlabs";
const providerLabels = {
elevenlabs: "Scribe (ElevenLabs)",
soniox: "Soniox",
local: "Whisper local",
};
const lbl = providerLabels[provider] || provider;
if (!confirm(`Ponovim transkript z "${lbl}"?\n\nObstoječi izrez (start/end) ostane enak. Trenutni MP4 + podnapisi se zbrišejo in regenerirajo.`)) return;
const btn = document.getElementById("retranscribe-btn");
const status = document.getElementById("edit-status");
if (btn) { btn.disabled = true; btn.textContent = "⏳ V vrsti…"; }
if (status) { status.textContent = `Pošiljam zahtevo (${lbl})…`; status.style.color = "#ffd700"; }
try {
const r = await fetch(`/api/jobs/${id}/retranscribe`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ provider, auto_upload: false }),
});
if (!r.ok) {
const err = await r.json().catch(() => ({}));
throw new Error(err.detail || `HTTP ${r.status}`);
}
const data = await r.json();
if (status) {
status.textContent = `✅ Job dodan v vrsto (poskus #${data.retranscribe_count}). Modal lahko zapreš — napredek bo viden v listi.`;
status.style.color = "#4ade80";
}
// Auto-close modal po 1.5s + refresh
setTimeout(() => {
closeModal();
refreshJobs();
// Watch job
if (typeof watchJob === "function") watchJob(id);
}, 1500);
} catch (e) {
if (status) {
status.textContent = `❌ Napaka: ${e.message}`;
status.style.color = "#ff6b6b";
}
if (btn) { btn.disabled = false; btn.textContent = "🔁 Ponovi transkript"; }
}
}
// ─── EDIT MODAL ─────────────────────────────────────
function formatTime(sec) {
if (!isFinite(sec) || sec < 0) sec = 0;
@@ -1509,8 +1554,16 @@
</div>
<div id="preview-status" style="margin-top:6px; font-size:12px; color:var(--muted); text-align:right;"></div>
<div class="modal-actions" style="margin-top:18px;">
<div class="modal-actions" style="margin-top:18px; display:flex; gap:8px; flex-wrap:wrap;">
<button class="primary" id="edit-save-btn">✅ Shrani in re-render</button>
<div style="display:flex; gap:6px; align-items:center; margin-left:auto;">
<select id="retranscribe-provider" style="padding:6px 8px; font-size:12px; background:var(--panel); color:var(--text); border:1px solid #444; border-radius:4px;" title="STT provider za ponovni transkript">
<option value="elevenlabs">Scribe (ElevenLabs)</option>
<option value="soniox">Soniox</option>
<option value="local">Whisper (lokalno)</option>
</select>
<button class="small ghost" id="retranscribe-btn" onclick="retranscribeJob('${jobId}')" title="Ponovi STT z izbranim providerjem in re-renderaj. Ohrani isti izrez." style="border-color:#ffd700; color:#ffd700;">🔁 Ponovi transkript</button>
</div>
<button onclick="closeModal()">Prekliči</button>
</div>
<div id="edit-status" style="margin-top:10px; font-size:12px; color:var(--muted);"></div>