From e9e7df7e201863dd7b07343db5a3826f2e2a167e Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Mon, 15 Jun 2026 06:17:50 +0200 Subject: [PATCH] Feat: Enhance library management features - Adds strict HTTP/HTTPS validation when creating library websites. - Improves summary cleanup in video ingestion by removing thinking tags and prefixes. - Updates LibraryManager UI to display comprehensive metadata (ID, provider, upload date, character count, etc.) and improves video source handling. --- backend/local_rag.py | 5 ++++- backend/video_ingest.py | 3 ++- src/LibraryManager.jsx | 12 ++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/backend/local_rag.py b/backend/local_rag.py index 38d99a2..e1be190 100644 --- a/backend/local_rag.py +++ b/backend/local_rag.py @@ -14,7 +14,7 @@ from concurrent.futures import ThreadPoolExecutor from datetime import datetime from pathlib import Path from typing import Any, Dict, List, Optional -from urllib.parse import quote +from urllib.parse import quote, urlparse import httpx from fastapi import APIRouter, HTTPException @@ -1599,6 +1599,9 @@ async def update_library_text(slug: str, item_id: str, req: UpdateTextRequest): @router.post("/libraries/{slug}/websites") async def create_library_website(slug: str, req: CreateWebsiteRequest): requested_url = str(req.url or "").strip() + parsed_url = urlparse(requested_url) + if parsed_url.scheme not in {"http", "https"} or not parsed_url.netloc: + raise HTTPException(status_code=400, detail="Enter a valid HTTP or HTTPS URL.") try: video_metadata = await asyncio.to_thread(probe_video_url, requested_url) except UnsupportedVideoUrl: diff --git a/backend/video_ingest.py b/backend/video_ingest.py index 22e9c5c..d8cf992 100644 --- a/backend/video_ingest.py +++ b/backend/video_ingest.py @@ -351,7 +351,8 @@ async def _summarize(title: str, transcript: str, model_name: str) -> str: ], options={"num_ctx": _choose_num_ctx(prompt)}, ) - clean = str(summary or "").strip() + clean = re.sub(r".*?", "", str(summary or ""), flags=re.DOTALL | re.IGNORECASE).strip() + clean = re.sub(r"^\s*Summary:\s*", "", clean, flags=re.IGNORECASE).strip() if not clean: raise RuntimeError("Ollama returned an empty video summary.") return clean diff --git a/src/LibraryManager.jsx b/src/LibraryManager.jsx index 55d4883..92c2270 100644 --- a/src/LibraryManager.jsx +++ b/src/LibraryManager.jsx @@ -484,7 +484,7 @@ export default function LibraryManager({ apiBase, library, jobs, onOpenChatMessa const source = item.kind === 'website' ? item.url : item.kind === 'video' - ? `${item.channel ? `${item.channel} · ` : ''}${item.duration_text || item.url}` + ? `${item.channel ? `${item.channel} · ` : ''}${item.duration_text || 'Saved video'}` : item.kind === 'text' ? 'Written in Heimgeist' : item.kind === 'chat_message' @@ -514,11 +514,19 @@ export default function LibraryManager({ apiBase, library, jobs, onOpenChatMessa
Video summary

{item.video_summary}

+ {item.extractor && Provider: {item.extractor}} + {item.video_id && Video ID: {item.video_id}} + {item.upload_date && Uploaded: {item.upload_date}} {item.transcription_model && Whisper: {item.transcription_model}} {item.transcription_workers && Workers: {item.transcription_workers}} + {item.transcription_slices && Audio slices: {item.transcription_slices}} + {item.transcript_chars && Transcript: {Number(item.transcript_chars).toLocaleString()} characters} {item.summary_model && Summary: {item.summary_model}} {item.yt_dlp_version && yt-dlp: {item.yt_dlp_version}} + {item.fetched_at && Processed: {new Date(item.fetched_at).toLocaleString()}}
+ {item.url &&
Source: {item.url}
} + {item.thumbnail_url && } )}
@@ -613,7 +621,7 @@ export default function LibraryManager({ apiBase, library, jobs, onOpenChatMessa
- + {items.length > 0 && hasMissingMetadata && !isSyncing && isReadyForChat && ( )}