diff --git a/backend/handlers/api.go b/backend/handlers/api.go index 644d2e3..d7d95a7 100644 --- a/backend/handlers/api.go +++ b/backend/handlers/api.go @@ -259,13 +259,16 @@ func (a *App) searchMedia(c *gin.Context) { return } + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "expanding query with Gemini", "progress": 10}) queryVariants, expandErr := a.GeminiService.ExpandQuery(req.Query) if len(queryVariants) == 0 { queryVariants = []string{req.Query} } + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "searching Google Video, Envato, and Artgrid", "progress": 35}) results, err := a.SearchService.SearchMedia(queryVariants) if err != nil { + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "search failed", "progress": 100, "message": err.Error()}) c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()}) return } @@ -274,12 +277,15 @@ func (a *App) searchMedia(c *gin.Context) { if expandErr != nil { warning += " Query expansion failed: " + expandErr.Error() } + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "no renderable search results", "progress": 100, "message": warning}) c.JSON(http.StatusOK, gin.H{"results": []services.AIRecommendation{}, "warning": warning}) return } + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "ranking thumbnail candidates", "progress": 55}) scored := rankSearchResults(req.Query, results) shortlist := scored[:min(len(scored), 10)] + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "analyzing shortlisted thumbnails with Gemini Vision", "progress": 75}) recommended, err := a.GeminiService.Recommend(req.Query, shortlist) if err != nil { fallback := make([]services.AIRecommendation, 0, min(20, len(scored))) @@ -297,6 +303,7 @@ func (a *App) searchMedia(c *gin.Context) { if expandErr != nil { warning = warning + " Query expansion failed: " + expandErr.Error() } + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "Gemini Vision fallback to ranked results", "progress": 90, "message": warning}) c.JSON(http.StatusOK, gin.H{"results": fallback, "warning": warning, "queries": queryVariants}) return } @@ -305,6 +312,7 @@ func (a *App) searchMedia(c *gin.Context) { if expandErr != nil { response["warning"] = "Gemini query expansion failed: " + expandErr.Error() + ". Using the original query only." } + a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "search complete", "progress": 100}) c.JSON(http.StatusOK, response) } diff --git a/frontend/app.js b/frontend/app.js index 7d79615..a6df659 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -13,6 +13,7 @@ const downloadUrl = document.getElementById("downloadUrl"); const downloadResult = document.getElementById("downloadResult"); const cardTemplate = document.getElementById("searchCardTemplate"); const previewModal = document.getElementById("previewModal"); +const previewMediaFrame = document.getElementById("previewMediaFrame"); const previewTitle = document.getElementById("previewTitle"); const previewVideo = document.getElementById("previewVideo"); const previewThumbnail = document.getElementById("previewThumbnail"); @@ -80,7 +81,8 @@ function connectWS() { return; } const data = payload.data; - setStatus(`${data.type || "task"}: ${data.status}`, Number(data.progress ?? 0)); + const label = data.status || `${data.type || "task"} in progress`; + setStatus(label, Number(data.progress ?? 0)); if (data.type === "upload" && data.status === "completed") { uploadResult.textContent = `${data.filename} saved successfully`; } @@ -128,7 +130,7 @@ function renderResults(results) { searchForm.addEventListener("submit", async (event) => { event.preventDefault(); - setStatus("searching", 20); + setStatus("preparing search", 5); searchWarning.classList.add("hidden"); try { const data = await api("/api/search", { @@ -169,6 +171,7 @@ function openPreviewModal(preview) { previewVideo.pause(); previewVideo.removeAttribute("src"); previewVideo.load(); + previewMediaFrame.style.aspectRatio = ""; if (preview.previewStreamUrl) { previewVideo.src = preview.previewStreamUrl; previewVideo.classList.remove("hidden"); @@ -199,6 +202,7 @@ function closeModal() { previewVideo.pause(); previewVideo.removeAttribute("src"); previewVideo.load(); + previewMediaFrame.style.aspectRatio = ""; previewModal.classList.add("hidden"); previewModal.classList.remove("flex"); startRange.value = "0"; @@ -298,6 +302,16 @@ setEndFromPreview.addEventListener("click", () => { endRange.value = String(Math.floor(previewVideo.currentTime || 0)); syncRanges(); }); +previewVideo.addEventListener("loadedmetadata", () => { + if (previewVideo.videoWidth > 0 && previewVideo.videoHeight > 0) { + previewMediaFrame.style.aspectRatio = `${previewVideo.videoWidth} / ${previewVideo.videoHeight}`; + } +}); +previewThumbnail.addEventListener("load", () => { + if (!previewVideo.src && previewThumbnail.naturalWidth > 0 && previewThumbnail.naturalHeight > 0) { + previewMediaFrame.style.aspectRatio = `${previewThumbnail.naturalWidth} / ${previewThumbnail.naturalHeight}`; + } +}); connectWS(); setStatus("idle", 0); diff --git a/frontend/index.html b/frontend/index.html index 43fd660..bc38159 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -79,9 +79,9 @@
-
- - +
+ +