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 && (
)}