Improve source-specific search queries and preview layout
build-push / docker (push) Successful in 4m14s
build-push / docker (push) Successful in 4m14s
This commit is contained in:
+52
-18
@@ -48,31 +48,34 @@ func (s *SearchService) SearchMedia(queries []string) ([]SearchResult, error) {
|
||||
}
|
||||
|
||||
sources := []struct {
|
||||
name string
|
||||
categories string
|
||||
engine string
|
||||
siteFilter string
|
||||
match func(string) bool
|
||||
name string
|
||||
categories string
|
||||
engine string
|
||||
queryBuilder func(string) string
|
||||
match func(string) bool
|
||||
}{
|
||||
{
|
||||
name: "Google Video",
|
||||
categories: "videos",
|
||||
engine: s.GoogleVideoEngine,
|
||||
match: func(string) bool { return true },
|
||||
queryBuilder: func(query string) string {
|
||||
return query
|
||||
},
|
||||
match: func(string) bool { return true },
|
||||
},
|
||||
{
|
||||
name: "Envato",
|
||||
categories: "general",
|
||||
engine: s.WebEngine,
|
||||
siteFilter: "site:elements.envato.com OR site:envato.com OR site:videohive.net",
|
||||
match: isEnvatoURL,
|
||||
name: "Envato",
|
||||
categories: "general",
|
||||
engine: s.WebEngine,
|
||||
queryBuilder: buildEnvatoQuery,
|
||||
match: isEnvatoURL,
|
||||
},
|
||||
{
|
||||
name: "Artgrid",
|
||||
categories: "general",
|
||||
engine: s.WebEngine,
|
||||
siteFilter: "site:artgrid.io",
|
||||
match: func(link string) bool { return strings.Contains(strings.ToLower(link), "artgrid.io") },
|
||||
name: "Artgrid",
|
||||
categories: "general",
|
||||
engine: s.WebEngine,
|
||||
queryBuilder: buildArtgridQuery,
|
||||
match: func(link string) bool { return strings.Contains(strings.ToLower(link), "artgrid.io") },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -86,8 +89,8 @@ func (s *SearchService) SearchMedia(queries []string) ([]SearchResult, error) {
|
||||
}
|
||||
for _, source := range sources {
|
||||
searchQuery := query
|
||||
if source.siteFilter != "" {
|
||||
searchQuery = query + " " + source.siteFilter
|
||||
if source.queryBuilder != nil {
|
||||
searchQuery = source.queryBuilder(query)
|
||||
}
|
||||
|
||||
items, err := s.search(searchQuery, source.categories, source.engine, source.name)
|
||||
@@ -112,6 +115,9 @@ func (s *SearchService) SearchMedia(queries []string) ([]SearchResult, error) {
|
||||
if source.match != nil && !source.match(item.Link) {
|
||||
continue
|
||||
}
|
||||
if !isRenderableLink(item.Link, item.Source) {
|
||||
continue
|
||||
}
|
||||
seen[item.Link] = true
|
||||
results = append(results, item)
|
||||
}
|
||||
@@ -224,6 +230,34 @@ func isEnvatoURL(link string) bool {
|
||||
return strings.Contains(lower, "envato") || strings.Contains(lower, "videohive.net")
|
||||
}
|
||||
|
||||
func isRenderableLink(link, source string) bool {
|
||||
parsed, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
path := strings.Trim(parsed.Path, "/")
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
lower := strings.ToLower(link)
|
||||
switch source {
|
||||
case "Envato":
|
||||
return strings.Contains(lower, "/item/") || strings.Contains(lower, "/stock-video/") || strings.Contains(lower, "/video-templates/")
|
||||
case "Artgrid":
|
||||
return strings.Contains(lower, "artgrid.io") && len(strings.Split(path, "/")) >= 2
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func buildEnvatoQuery(query string) string {
|
||||
return fmt.Sprintf(`%s ("stock video" OR footage OR "video template" OR cinematic) (site:elements.envato.com/stock-video OR site:elements.envato.com/video-templates OR site:videohive.net/item)`, query)
|
||||
}
|
||||
|
||||
func buildArtgridQuery(query string) string {
|
||||
return fmt.Sprintf(`%s ("stock footage" OR "b-roll" OR cinematic OR editorial) (site:artgrid.io)`, query)
|
||||
}
|
||||
|
||||
func deriveThumbnail(link string) string {
|
||||
if videoID := extractYouTubeID(link); videoID != "" {
|
||||
return "https://i.ytimg.com/vi/" + videoID + "/hqdefault.jpg"
|
||||
|
||||
@@ -43,6 +43,13 @@ func (g *GeminiService) ExpandQuery(query string) ([]string, error) {
|
||||
}
|
||||
|
||||
body := map[string]any{
|
||||
"systemInstruction": map[string]any{
|
||||
"parts": []map[string]string{
|
||||
{
|
||||
"text": "You are a JSON-only API. Output valid JSON only. Never add prose, labels, markdown, or explanations before or after the JSON.",
|
||||
},
|
||||
},
|
||||
},
|
||||
"contents": []map[string]any{
|
||||
{
|
||||
"parts": []map[string]string{
|
||||
@@ -85,6 +92,13 @@ User query: ` + query,
|
||||
jsonText, err := extractJSONObject(rawText)
|
||||
if err != nil {
|
||||
strictBody := map[string]any{
|
||||
"systemInstruction": map[string]any{
|
||||
"parts": []map[string]string{
|
||||
{
|
||||
"text": "You are a strict JSON emitter. Output one valid JSON object only. Do not write any other text.",
|
||||
},
|
||||
},
|
||||
},
|
||||
"contents": []map[string]any{
|
||||
{
|
||||
"parts": []map[string]string{
|
||||
|
||||
+5
-5
@@ -70,7 +70,7 @@
|
||||
</main>
|
||||
|
||||
<div id="previewModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/80 px-4">
|
||||
<div class="w-full max-w-3xl rounded-3xl border border-white/10 bg-zinc-950 p-5 shadow-2xl">
|
||||
<div class="w-full max-w-4xl rounded-3xl border border-white/10 bg-zinc-950 p-5 shadow-2xl">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Download Preview</p>
|
||||
@@ -78,12 +78,12 @@
|
||||
</div>
|
||||
<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 id="previewMediaFrame" class="flex min-h-[320px] items-center justify-center overflow-hidden rounded-3xl border border-white/10 bg-black/30 p-2">
|
||||
<div class="mt-5 grid gap-5 xl:grid-cols-[minmax(0,1.15fr)_360px]">
|
||||
<div id="previewMediaFrame" class="min-w-0 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="min-w-0 space-y-4">
|
||||
<div class="rounded-2xl border border-white/10 bg-white/[0.03] p-4">
|
||||
<div class="flex items-center justify-between text-sm text-zinc-400">
|
||||
<span>Detected duration</span>
|
||||
@@ -136,6 +136,6 @@
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script src="/app.js?v=20260313b" defer></script>
|
||||
<script src="/app.js?v=20260313c" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user