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.
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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"<think>.*?</think>", "", 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
|
||||
|
||||
@@ -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
|
||||
<div className="library-metadata-label">Video summary</div>
|
||||
<p>{item.video_summary}</p>
|
||||
<div className="library-metadata-details">
|
||||
{item.extractor && <span>Provider: {item.extractor}</span>}
|
||||
{item.video_id && <span>Video ID: {item.video_id}</span>}
|
||||
{item.upload_date && <span>Uploaded: {item.upload_date}</span>}
|
||||
{item.transcription_model && <span>Whisper: {item.transcription_model}</span>}
|
||||
{item.transcription_workers && <span>Workers: {item.transcription_workers}</span>}
|
||||
{item.transcription_slices && <span>Audio slices: {item.transcription_slices}</span>}
|
||||
{item.transcript_chars && <span>Transcript: {Number(item.transcript_chars).toLocaleString()} characters</span>}
|
||||
{item.summary_model && <span>Summary: {item.summary_model}</span>}
|
||||
{item.yt_dlp_version && <span>yt-dlp: {item.yt_dlp_version}</span>}
|
||||
{item.fetched_at && <span>Processed: {new Date(item.fetched_at).toLocaleString()}</span>}
|
||||
</div>
|
||||
{item.url && <div className="library-video-source">Source: {item.url}</div>}
|
||||
{item.thumbnail_url && <button type="button" className="button ghost library-video-thumbnail" onClick={() => desktopApi.openExternalLink(item.thumbnail_url)}>Open thumbnail</button>}
|
||||
</div>
|
||||
)}
|
||||
<div className={`library-item-metadata status-${metadata.status || 'missing'}`}>
|
||||
@@ -613,7 +621,7 @@ export default function LibraryManager({ apiBase, library, jobs, onOpenChatMessa
|
||||
<div className="library-footer-actions">
|
||||
<button className="button" disabled={busy} onClick={addPaths}>Add Files</button>
|
||||
<button className="button" disabled={busy} onClick={() => { closeForm(); setFormMode('text') }}>Add Text</button>
|
||||
<button className="button" disabled={busy} onClick={() => { closeForm(); setFormMode('website') }}>Add Website</button>
|
||||
<button className="button" disabled={busy} onClick={() => { closeForm(); setFormMode('website') }}>Add Website / Video</button>
|
||||
{items.length > 0 && hasMissingMetadata && !isSyncing && isReadyForChat && (
|
||||
<button className="button ghost" disabled={busy} onClick={generateMetadata}>Generate Metadata</button>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user