This commit is contained in:
+50
-10
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
neturl "net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -201,15 +202,17 @@ func (g *GeminiService) TranslateQuery(query string) string {
|
||||
}
|
||||
|
||||
rawText, err := g.generateText(body)
|
||||
if err != nil {
|
||||
return strings.TrimSpace(query)
|
||||
if err == nil {
|
||||
translated := sanitizePlainEnglishLine(rawText)
|
||||
if translated != "" && !strings.EqualFold(translated, strings.TrimSpace(query)) {
|
||||
return translated
|
||||
}
|
||||
}
|
||||
|
||||
translated := sanitizePlainEnglishLine(rawText)
|
||||
if translated == "" {
|
||||
return strings.TrimSpace(query)
|
||||
if translated, err := g.translateViaGoogle(query); err == nil && translated != "" {
|
||||
return translated
|
||||
}
|
||||
return translated
|
||||
return strings.TrimSpace(query)
|
||||
}
|
||||
|
||||
func (g *GeminiService) generateText(body map[string]any) (string, error) {
|
||||
@@ -464,10 +467,6 @@ func fallbackQueryExpansion(originalQuery, englishQuery string) []string {
|
||||
base + " 4k footage",
|
||||
base + " cinematic b-roll",
|
||||
}
|
||||
if strings.TrimSpace(originalQuery) != "" && !strings.EqualFold(strings.TrimSpace(originalQuery), strings.TrimSpace(englishQuery)) {
|
||||
candidates = append(candidates, strings.TrimSpace(originalQuery))
|
||||
}
|
||||
|
||||
seen := map[string]bool{}
|
||||
queries := make([]string, 0, len(candidates))
|
||||
for _, item := range candidates {
|
||||
@@ -519,3 +518,44 @@ func looksMostlyASCII(text string) bool {
|
||||
}
|
||||
return ascii >= len(runes)*8/10
|
||||
}
|
||||
|
||||
func (g *GeminiService) translateViaGoogle(query string) (string, error) {
|
||||
endpoint := "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=en&dt=t&q=" + neturl.QueryEscape(query)
|
||||
resp, err := g.Client.Get(endpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
return "", fmt.Errorf("google translate fallback returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var payload []any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(payload) == 0 {
|
||||
return "", fmt.Errorf("google translate fallback returned no payload")
|
||||
}
|
||||
top, ok := payload[0].([]any)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("google translate fallback returned unexpected payload")
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
for _, part := range top {
|
||||
segment, ok := part.([]any)
|
||||
if !ok || len(segment) == 0 {
|
||||
continue
|
||||
}
|
||||
if text, ok := segment[0].(string); ok {
|
||||
builder.WriteString(text)
|
||||
}
|
||||
}
|
||||
translated := strings.TrimSpace(builder.String())
|
||||
if translated == "" {
|
||||
return "", fmt.Errorf("google translate fallback returned empty translation")
|
||||
}
|
||||
return translated, nil
|
||||
}
|
||||
|
||||
+20
-20
@@ -8,12 +8,12 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
</head>
|
||||
<body class="min-h-full bg-zinc-950 text-zinc-100 selection:bg-white selection:text-black">
|
||||
<main class="mx-auto flex min-h-screen max-w-7xl flex-col gap-6 px-4 py-6 lg:px-8">
|
||||
<header class="rounded-3xl border border-white/10 bg-white/5 p-6 backdrop-blur">
|
||||
<main class="mx-auto flex min-h-screen max-w-[1560px] flex-col gap-8 px-5 py-8 lg:px-10">
|
||||
<header class="rounded-3xl border border-white/10 bg-white/5 p-8 backdrop-blur">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs uppercase tracking-[0.4em] text-zinc-500">AI Media Asset Ingest Hub</p>
|
||||
<h1 class="mt-3 text-3xl font-semibold tracking-tight text-white">SAVE THE NURSE AI Search</h1>
|
||||
<h1 class="mt-3 text-4xl font-semibold tracking-tight text-white">SAVE THE NURSE AI Search</h1>
|
||||
</div>
|
||||
<div class="w-full max-w-md">
|
||||
<div class="mb-2 flex items-center justify-between text-xs uppercase tracking-[0.3em] text-zinc-500">
|
||||
@@ -27,41 +27,41 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="grid gap-6 lg:grid-cols-[1.2fr_0.9fr]">
|
||||
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-5">
|
||||
<section class="grid gap-8 lg:grid-cols-[1.3fr_0.95fr]">
|
||||
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-7">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone A</p>
|
||||
<h2 class="text-xl font-semibold text-white">AI Smart Discovery</h2>
|
||||
<h2 class="text-2xl font-semibold text-white">AI Smart Discovery</h2>
|
||||
</div>
|
||||
</div>
|
||||
<form id="searchForm" class="flex flex-col gap-3 md:flex-row">
|
||||
<input id="searchQuery" type="text" placeholder="한글 검색어를 입력하세요" class="flex-1 rounded-2xl border border-white/10 bg-black/40 px-4 py-3 text-sm text-white outline-none ring-0 placeholder:text-zinc-500" />
|
||||
<button class="rounded-2xl border border-white bg-white px-5 py-3 text-sm font-medium text-black transition hover:bg-zinc-200">AI Search</button>
|
||||
<input id="searchQuery" type="text" placeholder="한글 검색어를 입력하세요" class="flex-1 rounded-2xl border border-white/10 bg-black/40 px-5 py-4 text-base text-white outline-none ring-0 placeholder:text-zinc-500" />
|
||||
<button class="rounded-2xl border border-white bg-white px-7 py-4 text-base font-medium text-black transition hover:bg-zinc-200">AI Search</button>
|
||||
</form>
|
||||
<div id="searchWarning" class="mt-3 hidden rounded-2xl border border-amber-500/30 bg-amber-500/10 px-4 py-3 text-sm text-amber-200"></div>
|
||||
<div id="queryVariants" class="mt-3 hidden flex-wrap gap-2"></div>
|
||||
<div id="searchResults" class="mt-5 grid gap-4 sm:grid-cols-2 xl:grid-cols-3"></div>
|
||||
<div id="searchResults" class="mt-6 grid gap-5 sm:grid-cols-2 xl:grid-cols-3"></div>
|
||||
</article>
|
||||
|
||||
<div class="grid gap-6">
|
||||
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-5">
|
||||
<div class="grid gap-8">
|
||||
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-7">
|
||||
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone B</p>
|
||||
<h2 class="text-xl font-semibold text-white">Smart Ingest Dropzone</h2>
|
||||
<h2 class="text-2xl font-semibold text-white">Smart Ingest Dropzone</h2>
|
||||
<label id="dropzone" class="mt-4 flex min-h-64 cursor-pointer flex-col items-center justify-center rounded-3xl border border-dashed border-white/20 bg-black/30 p-6 text-center transition hover:border-white/50 hover:bg-white/[0.05]">
|
||||
<input id="fileInput" type="file" class="hidden" />
|
||||
<span class="text-lg font-medium text-white">Drop file here</span>
|
||||
<span class="mt-2 text-sm text-zinc-400">or click to upload into /app/downloads</span>
|
||||
<span class="text-2xl font-medium text-white">Drop file here</span>
|
||||
<span class="mt-2 text-base text-zinc-400">or click to upload into /app/downloads</span>
|
||||
</label>
|
||||
<p id="uploadResult" class="mt-3 text-sm text-zinc-400"></p>
|
||||
</article>
|
||||
|
||||
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-5">
|
||||
<article class="rounded-3xl border border-white/10 bg-white/[0.03] p-7">
|
||||
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Zone C</p>
|
||||
<h2 class="text-xl font-semibold text-white">Direct Downloader & Crop</h2>
|
||||
<h2 class="text-2xl font-semibold text-white">Direct Downloader & Crop</h2>
|
||||
<form id="downloadForm" class="mt-4 space-y-3">
|
||||
<input id="downloadUrl" type="url" placeholder="https://..." class="w-full rounded-2xl border border-white/10 bg-black/40 px-4 py-3 text-sm text-white placeholder:text-zinc-500" />
|
||||
<button class="w-full rounded-2xl border border-white px-5 py-3 text-sm font-medium text-white transition hover:bg-white hover:text-black">Preview & Queue</button>
|
||||
<input id="downloadUrl" type="url" placeholder="https://..." class="w-full rounded-2xl border border-white/10 bg-black/40 px-5 py-4 text-base text-white placeholder:text-zinc-500" />
|
||||
<button class="w-full rounded-2xl border border-white px-6 py-4 text-base font-medium text-white transition hover:bg-white hover:text-black">Preview & Queue</button>
|
||||
</form>
|
||||
<p id="downloadResult" class="mt-3 text-sm text-zinc-400"></p>
|
||||
</article>
|
||||
@@ -131,8 +131,8 @@
|
||||
<div class="absolute left-3 top-3 rounded-full border border-white/20 bg-black/60 px-3 py-1 text-[11px] uppercase tracking-[0.25em] text-white">AI Recommended</div>
|
||||
<div class="source-badge absolute bottom-3 left-3 rounded-full bg-white px-3 py-1 text-[11px] font-medium uppercase tracking-[0.2em] text-black"></div>
|
||||
</div>
|
||||
<div class="space-y-2 p-4">
|
||||
<h3 class="line-clamp-2 text-sm font-medium text-white"></h3>
|
||||
<div class="space-y-2 p-5">
|
||||
<h3 class="line-clamp-2 text-base font-medium text-white"></h3>
|
||||
<p class="line-clamp-3 text-sm text-zinc-400"></p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user