From f5d03627be5d53584fd055edf15df74b01d09bba Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Mon, 16 Mar 2026 15:03:44 +0900 Subject: [PATCH] Backfill partial Gemini results and restore modal iframe --- TODO.md | 15 +++++++++++++++ backend/handlers/api.go | 8 ++++++++ backend/services/ranker.go | 32 ++++++++++++++++++++++++++++++++ frontend/app.js | 4 +--- frontend/index.html | 2 +- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index c90e676..9b2e453 100644 --- a/TODO.md +++ b/TODO.md @@ -460,6 +460,21 @@ - There is one known local commit that has not been pushed because the remote repo reported an unpacker error. ## Recent Change Log +- Date: `2026-03-16` +- What changed: + - When Gemini batch evaluation is only partially successful, the API now explicitly backfills the final list with preview-capable ranked candidates instead of leaving the visible result set too thin. + - Result modal behavior was switched so Envato and Artgrid open their actual item webpages inside the modal iframe again, while Google Video still uses the YouTube embed path. + - Frontend asset version was bumped again to force clients off stale preview-handling code. +- Why it changed: + - The new debug log showed `recommendedCount: 2` plus one failed Gemini batch, but the final visible list still collapsed to `6` items even though the raw pool was `25`. + - The user wants in-modal login/download for Envato and Artgrid rather than preview-only media mode. +- How it was verified: + - log review from `ai-media-hub-2026-03-16T06-00-31-359Z.log` + - code inspection of post-Gemini backfill and modal rendering path +- What is still risky or incomplete: + - Artgrid candidate volume is still often low because SearXNG is returning many `0`-result queries for current Artgrid query templates. + - If a browser still holds older frontend assets, a hard refresh may be required before the latest modal/preview behavior appears. + - Date: `2026-03-16` - What changed: - Significantly increased backend debug logging volume and routed it into the existing WebSocket `debug` stream so the in-app `Logs` panel captures much deeper request, search, preview, Gemini, and ranking traces. diff --git a/backend/handlers/api.go b/backend/handlers/api.go index 80dd192..1b47b23 100644 --- a/backend/handlers/api.go +++ b/backend/handlers/api.go @@ -458,6 +458,14 @@ func (a *App) searchMedia(c *gin.Context) { } merged := services.MergeRecommendations(recommended, scored, 20) + if geminiErr != nil { + merged = services.BackfillRecommendations( + merged, + scored, + 12, + "Gemini 배치 일부가 실패해 미리보기 가능한 상위 후보를 보강했습니다.", + ) + } merged = services.RandomizeTopRecommendations(merged, 8) warning := "" if geminiErr != nil { diff --git a/backend/services/ranker.go b/backend/services/ranker.go index 8b65bdf..8871181 100644 --- a/backend/services/ranker.go +++ b/backend/services/ranker.go @@ -355,6 +355,38 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult, return merged } +func BackfillRecommendations(existing []AIRecommendation, ranked []SearchResult, limit int, reason string) []AIRecommendation { + merged := make([]AIRecommendation, 0, min(limit, len(ranked))) + seen := map[string]bool{} + for _, item := range existing { + if item.Link == "" || seen[item.Link] { + continue + } + seen[item.Link] = true + merged = append(merged, item) + } + for _, item := range ranked { + if len(merged) >= limit || item.Link == "" || seen[item.Link] { + continue + } + if strings.TrimSpace(item.ThumbnailURL) == "" && strings.TrimSpace(item.PreviewVideoURL) == "" { + continue + } + seen[item.Link] = true + merged = append(merged, AIRecommendation{ + Title: item.Title, + Link: item.Link, + Snippet: item.Snippet, + ThumbnailURL: item.ThumbnailURL, + PreviewVideoURL: item.PreviewVideoURL, + Source: item.Source, + Reason: reason, + Recommended: false, + }) + } + return merged +} + func max(a, b int) int { if a > b { return a diff --git a/frontend/app.js b/frontend/app.js index 233b959..923a1e7 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -513,10 +513,8 @@ function openResultModal(item) { resetResultModalMedia(); if (item.source === "Google Video") { showResultModalFrame(buildResultModalEmbedURL(item)); - } else if (item.previewVideoUrl) { - showResultModalVideo(item.previewVideoUrl); } else { - showResultModalThumbnail(item.thumbnailUrl, item.title || ""); + showResultModalFrame(item.link || "about:blank"); } showModal(resultModal); logEvent("result:modal:open", { title: item.title, source: item.source, link: item.link }); diff --git a/frontend/index.html b/frontend/index.html index 2b0c77b..954d6b4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -202,6 +202,6 @@ - +