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 {
|
sources := []struct {
|
||||||
name string
|
name string
|
||||||
categories string
|
categories string
|
||||||
engine string
|
engine string
|
||||||
siteFilter string
|
queryBuilder func(string) string
|
||||||
match func(string) bool
|
match func(string) bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Google Video",
|
name: "Google Video",
|
||||||
categories: "videos",
|
categories: "videos",
|
||||||
engine: s.GoogleVideoEngine,
|
engine: s.GoogleVideoEngine,
|
||||||
match: func(string) bool { return true },
|
queryBuilder: func(query string) string {
|
||||||
|
return query
|
||||||
|
},
|
||||||
|
match: func(string) bool { return true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Envato",
|
name: "Envato",
|
||||||
categories: "general",
|
categories: "general",
|
||||||
engine: s.WebEngine,
|
engine: s.WebEngine,
|
||||||
siteFilter: "site:elements.envato.com OR site:envato.com OR site:videohive.net",
|
queryBuilder: buildEnvatoQuery,
|
||||||
match: isEnvatoURL,
|
match: isEnvatoURL,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Artgrid",
|
name: "Artgrid",
|
||||||
categories: "general",
|
categories: "general",
|
||||||
engine: s.WebEngine,
|
engine: s.WebEngine,
|
||||||
siteFilter: "site:artgrid.io",
|
queryBuilder: buildArtgridQuery,
|
||||||
match: func(link string) bool { return strings.Contains(strings.ToLower(link), "artgrid.io") },
|
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 {
|
for _, source := range sources {
|
||||||
searchQuery := query
|
searchQuery := query
|
||||||
if source.siteFilter != "" {
|
if source.queryBuilder != nil {
|
||||||
searchQuery = query + " " + source.siteFilter
|
searchQuery = source.queryBuilder(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err := s.search(searchQuery, source.categories, source.engine, source.name)
|
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) {
|
if source.match != nil && !source.match(item.Link) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !isRenderableLink(item.Link, item.Source) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
seen[item.Link] = true
|
seen[item.Link] = true
|
||||||
results = append(results, item)
|
results = append(results, item)
|
||||||
}
|
}
|
||||||
@@ -224,6 +230,34 @@ func isEnvatoURL(link string) bool {
|
|||||||
return strings.Contains(lower, "envato") || strings.Contains(lower, "videohive.net")
|
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 {
|
func deriveThumbnail(link string) string {
|
||||||
if videoID := extractYouTubeID(link); videoID != "" {
|
if videoID := extractYouTubeID(link); videoID != "" {
|
||||||
return "https://i.ytimg.com/vi/" + videoID + "/hqdefault.jpg"
|
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{
|
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{
|
"contents": []map[string]any{
|
||||||
{
|
{
|
||||||
"parts": []map[string]string{
|
"parts": []map[string]string{
|
||||||
@@ -85,6 +92,13 @@ User query: ` + query,
|
|||||||
jsonText, err := extractJSONObject(rawText)
|
jsonText, err := extractJSONObject(rawText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
strictBody := map[string]any{
|
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{
|
"contents": []map[string]any{
|
||||||
{
|
{
|
||||||
"parts": []map[string]string{
|
"parts": []map[string]string{
|
||||||
|
|||||||
+5
-5
@@ -70,7 +70,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="previewModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/80 px-4">
|
<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 class="flex items-start justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Download Preview</p>
|
<p class="text-xs uppercase tracking-[0.3em] text-zinc-500">Download Preview</p>
|
||||||
@@ -78,12 +78,12 @@
|
|||||||
</div>
|
</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>
|
<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>
|
||||||
<div class="mt-5 grid gap-5 md:grid-cols-[1.2fr_0.8fr]">
|
<div class="mt-5 grid gap-5 xl:grid-cols-[minmax(0,1.15fr)_360px]">
|
||||||
<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 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>
|
<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="" />
|
<img id="previewThumbnail" class="max-h-[60vh] w-full object-contain" alt="" />
|
||||||
</div>
|
</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="rounded-2xl border border-white/10 bg-white/[0.03] p-4">
|
||||||
<div class="flex items-center justify-between text-sm text-zinc-400">
|
<div class="flex items-center justify-between text-sm text-zinc-400">
|
||||||
<span>Detected duration</span>
|
<span>Detected duration</span>
|
||||||
@@ -136,6 +136,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="/app.js?v=20260313b" defer></script>
|
<script src="/app.js?v=20260313c" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user