This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+16
-2
@@ -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);
|
||||
|
||||
+3
-3
@@ -79,9 +79,9 @@
|
||||
<button id="closePreviewModal" class="rounded-full border border-white/10 px-3 py-2 text-xs uppercase tracking-[0.2em] text-zinc-300">Close</button>
|
||||
</div>
|
||||
<div class="mt-5 grid gap-5 md:grid-cols-[1.2fr_0.8fr]">
|
||||
<div class="overflow-hidden rounded-3xl border border-white/10 bg-black/30">
|
||||
<video id="previewVideo" class="hidden aspect-video h-full w-full bg-black object-cover" controls playsinline></video>
|
||||
<img id="previewThumbnail" class="aspect-video h-full w-full object-cover" alt="" />
|
||||
<div id="previewMediaFrame" class="flex min-h-[320px] items-center justify-center overflow-hidden rounded-3xl border border-white/10 bg-black/30 p-2">
|
||||
<video id="previewVideo" class="hidden max-h-[60vh] w-full bg-black object-contain" controls playsinline></video>
|
||||
<img id="previewThumbnail" class="max-h-[60vh] w-full object-contain" alt="" />
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="rounded-2xl border border-white/10 bg-white/[0.03] p-4">
|
||||
|
||||
Reference in New Issue
Block a user