diff --git a/api/router.py b/api/router.py index 4eabfcc..2118769 100644 --- a/api/router.py +++ b/api/router.py @@ -134,6 +134,35 @@ async def get_transcript(filename: str, user: dict = Depends(current_user)): return PlainTextResponse(content) +@router.post("/transcripts/{filename}/reprocess") +async def reprocess_transcript(filename: str, body: dict, user: dict = Depends(current_user)): + from output import read_transcript + from fastapi.responses import PlainTextResponse + from llm import OllamaClient + user_dir = os.path.join(user["output_dir"], user["username"]) + content = read_transcript(user_dir, filename) + if content is None: + raise HTTPException(status_code=404, detail="Nicht gefunden") + # Strip YAML frontmatter before sending to LLM + body_text = content + if content.startswith("---\n"): + end = content.find("\n---\n", 4) + if end != -1: + body_text = content[end + 5:].lstrip("\n") + cfg = load_config() + instructions = body.get("instructions", "") + client = OllamaClient(base_url=cfg["ollama"]["base_url"]) + refined = await client.refine(body_text, instructions=instructions, model=cfg["ollama"]["model"]) + # Overwrite same file (keep filename stable, update frontmatter date) + from datetime import datetime + path = os.path.join(user_dir, filename) + with open(path, "w", encoding="utf-8") as f: + now = datetime.now() + f.write(f"---\ndate: {now.isoformat(timespec='seconds')}\ntags: [transkript]\n---\n\n") + f.write(refined if refined.endswith("\n") else refined + "\n") + return PlainTextResponse(refined) + + @router.delete("/transcripts/{filename}") async def delete_transcript(filename: str, user: dict = Depends(current_user)): user_dir = os.path.join(user["output_dir"], user["username"]) diff --git a/frontend/app.js b/frontend/app.js index 0943af9..59ab963 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -10,7 +10,9 @@ const modalTitle = document.getElementById('modal-title'); const modalBody = document.getElementById('modal-body'); const modalOpenBtn = document.getElementById('modal-open-btn'); const modalCloseBtn = document.getElementById('modal-close-btn'); +const modalReprocessBtn = document.getElementById('modal-reprocess-btn'); let _modalPath = null; +let _modalFilename = null; const STATUS_LABELS = { idle: 'Bereit', @@ -43,6 +45,7 @@ logoutBtn.addEventListener('click', () => { function openModal(filename, path) { _modalPath = path; + _modalFilename = filename; modalTitle.textContent = filename.replace(/\.md$/, '').replace(/^\d{4}-\d{2}-\d{2}-\d{4}-/, ''); modalBody.innerHTML = ''; modal.classList.remove('hidden'); @@ -56,6 +59,7 @@ function openModal(filename, path) { function closeModal() { modal.classList.add('hidden'); _modalPath = null; + _modalFilename = null; } modalCloseBtn.addEventListener('click', closeModal); @@ -65,6 +69,20 @@ modalOpenBtn.addEventListener('click', () => { if (_modalPath) apiFetch('/open', { method: 'POST', body: JSON.stringify({ path: _modalPath }) }); }); +modalReprocessBtn.addEventListener('click', async () => { + if (!_modalFilename) return; + modalReprocessBtn.disabled = true; + modalBody.textContent = 'Wird neu verarbeitet\u2026'; + const instructions = instructionsEl.value; + const r = await apiFetch(`/transcripts/${encodeURIComponent(_modalFilename)}/reprocess`, { + method: 'POST', + body: JSON.stringify({ instructions }), + }); + const md = await r.text(); + modalBody.innerHTML = DOMPurify.sanitize(marked.parse(md)); + modalReprocessBtn.disabled = false; +}); + instructionsEl.addEventListener('input', async () => { await apiFetch('/instructions', { method: 'POST', diff --git a/frontend/index.html b/frontend/index.html index 4455148..b56de29 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -200,6 +200,11 @@