Backfill partial Gemini results and restore modal iframe
build-push / docker (push) Successful in 4m13s

This commit is contained in:
AI Assistant
2026-03-16 15:03:44 +09:00
parent cd9b47b33e
commit f5d03627be
5 changed files with 57 additions and 4 deletions
+15
View File
@@ -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.
+8
View File
@@ -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 {
+32
View File
@@ -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
+1 -3
View File
@@ -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 });
+1 -1
View File
@@ -202,6 +202,6 @@
</button>
</template>
<script src="/app.js?v=20260316e" defer></script>
<script src="/app.js?v=20260316f" defer></script>
</body>
</html>