feat: reprocess existing transcript via Ollama — modal button + POST /transcripts/{filename}/reprocess
This commit is contained in:
@@ -134,6 +134,35 @@ async def get_transcript(filename: str, user: dict = Depends(current_user)):
|
|||||||
return PlainTextResponse(content)
|
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}")
|
@router.delete("/transcripts/{filename}")
|
||||||
async def delete_transcript(filename: str, user: dict = Depends(current_user)):
|
async def delete_transcript(filename: str, user: dict = Depends(current_user)):
|
||||||
user_dir = os.path.join(user["output_dir"], user["username"])
|
user_dir = os.path.join(user["output_dir"], user["username"])
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ const modalTitle = document.getElementById('modal-title');
|
|||||||
const modalBody = document.getElementById('modal-body');
|
const modalBody = document.getElementById('modal-body');
|
||||||
const modalOpenBtn = document.getElementById('modal-open-btn');
|
const modalOpenBtn = document.getElementById('modal-open-btn');
|
||||||
const modalCloseBtn = document.getElementById('modal-close-btn');
|
const modalCloseBtn = document.getElementById('modal-close-btn');
|
||||||
|
const modalReprocessBtn = document.getElementById('modal-reprocess-btn');
|
||||||
let _modalPath = null;
|
let _modalPath = null;
|
||||||
|
let _modalFilename = null;
|
||||||
|
|
||||||
const STATUS_LABELS = {
|
const STATUS_LABELS = {
|
||||||
idle: 'Bereit',
|
idle: 'Bereit',
|
||||||
@@ -43,6 +45,7 @@ logoutBtn.addEventListener('click', () => {
|
|||||||
|
|
||||||
function openModal(filename, path) {
|
function openModal(filename, path) {
|
||||||
_modalPath = path;
|
_modalPath = path;
|
||||||
|
_modalFilename = filename;
|
||||||
modalTitle.textContent = filename.replace(/\.md$/, '').replace(/^\d{4}-\d{2}-\d{2}-\d{4}-/, '');
|
modalTitle.textContent = filename.replace(/\.md$/, '').replace(/^\d{4}-\d{2}-\d{2}-\d{4}-/, '');
|
||||||
modalBody.innerHTML = '';
|
modalBody.innerHTML = '';
|
||||||
modal.classList.remove('hidden');
|
modal.classList.remove('hidden');
|
||||||
@@ -56,6 +59,7 @@ function openModal(filename, path) {
|
|||||||
function closeModal() {
|
function closeModal() {
|
||||||
modal.classList.add('hidden');
|
modal.classList.add('hidden');
|
||||||
_modalPath = null;
|
_modalPath = null;
|
||||||
|
_modalFilename = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
modalCloseBtn.addEventListener('click', closeModal);
|
modalCloseBtn.addEventListener('click', closeModal);
|
||||||
@@ -65,6 +69,20 @@ modalOpenBtn.addEventListener('click', () => {
|
|||||||
if (_modalPath) apiFetch('/open', { method: 'POST', body: JSON.stringify({ path: _modalPath }) });
|
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 () => {
|
instructionsEl.addEventListener('input', async () => {
|
||||||
await apiFetch('/instructions', {
|
await apiFetch('/instructions', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -200,6 +200,11 @@
|
|||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span id="modal-title" class="modal-title"></span>
|
<span id="modal-title" class="modal-title"></span>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
|
<button id="modal-reprocess-btn" class="modal-btn" title="Neu verarbeiten">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 8 8h-2a6 6 0 1 1-1.76-4.24l-2.24 2.24H20V4l-2.35 2.35z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<button id="modal-open-btn" class="modal-btn" title="Im Editor öffnen">
|
<button id="modal-open-btn" class="modal-btn" title="Im Editor öffnen">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M14 3h7v7h-2V6.41l-9.29 9.3-1.42-1.42L17.59 5H14V3zm-1 2H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8h-2v8H5V7h8V5z"/>
|
<path d="M14 3h7v7h-2V6.41l-9.29 9.3-1.42-1.42L17.59 5H14V3zm-1 2H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8h-2v8H5V7h8V5z"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user