Search/images.go

160 lines
3.9 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"html/template"
"log"
2024-06-10 09:49:40 +00:00
"math/rand"
"net/http"
2024-06-10 09:49:40 +00:00
"sync"
"time"
)
2024-06-10 09:49:40 +00:00
var (
imageEngines []imageEngine
imageEngineLock sync.Mutex
)
2024-06-10 09:49:40 +00:00
type imageEngine struct {
Name string
Func func(string, string, string, int) ([]ImageSearchResult, error)
Weight int
2024-04-07 13:48:25 +00:00
}
2024-06-10 09:49:40 +00:00
func init() {
imageEngines = []imageEngine{
{Name: "Qwant", Func: PerformQwantImageSearch, Weight: 1},
{Name: "Imgur", Func: PerformImgurImageSearch, Weight: 2},
}
2024-06-10 09:49:40 +00:00
rand.Seed(time.Now().UnixNano())
}
2024-04-07 13:48:25 +00:00
func handleImageSearch(w http.ResponseWriter, query, safe, lang string, page int) {
startTime := time.Now()
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "image"}
combinedResults := getImageResultsFromCacheOrFetch(cacheKey, query, safe, lang, page)
elapsedTime := time.Since(startTime)
2024-04-07 13:48:25 +00:00
tmpl, err := template.New("images.html").Funcs(funcs).ParseFiles("templates/images.html")
if err != nil {
log.Printf("Error parsing template: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
data := struct {
2024-04-08 11:15:23 +00:00
Results []ImageSearchResult
Query string
Page int
Fetched string
LanguageOptions []LanguageOption
CurrentLang string
HasPrevPage bool
HasNextPage bool
}{
Results: combinedResults,
2024-04-08 11:15:23 +00:00
Query: query,
Page: page,
Fetched: fmt.Sprintf("%.2f seconds", elapsedTime.Seconds()),
2024-04-08 11:15:23 +00:00
LanguageOptions: languageOptions,
CurrentLang: lang,
HasPrevPage: page > 1,
HasNextPage: len(combinedResults) >= 50,
}
err = tmpl.Execute(w, data)
if err != nil {
log.Printf("Error executing template: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
func getImageResultsFromCacheOrFetch(cacheKey CacheKey, query, safe, lang string, page int) []ImageSearchResult {
cacheChan := make(chan []SearchResult)
var combinedResults []ImageSearchResult
go func() {
results, exists := resultsCache.Get(cacheKey)
if exists {
log.Println("Cache hit")
cacheChan <- results
} else {
log.Println("Cache miss")
cacheChan <- nil
}
}()
select {
case results := <-cacheChan:
if results == nil {
2024-06-10 09:49:40 +00:00
combinedResults = fetchImageResults(query, safe, lang, page)
if len(combinedResults) > 0 {
resultsCache.Set(cacheKey, convertToSearchResults(combinedResults))
}
} else {
_, _, imageResults := convertToSpecificResults(results)
combinedResults = imageResults
}
case <-time.After(2 * time.Second):
log.Println("Cache check timeout")
2024-06-10 09:49:40 +00:00
combinedResults = fetchImageResults(query, safe, lang, page)
if len(combinedResults) > 0 {
resultsCache.Set(cacheKey, convertToSearchResults(combinedResults))
}
}
return combinedResults
}
2024-06-10 09:49:40 +00:00
func fetchImageResults(query, safe, lang string, page int) []ImageSearchResult {
var results []ImageSearchResult
var err error
for attempts := 0; attempts < len(imageEngines); attempts++ {
engine := selectImageEngine()
log.Printf("Using image search engine: %s", engine.Name)
results, err = engine.Func(query, safe, lang, page)
if err != nil {
log.Printf("Error performing image search with %s: %v", engine.Name, err)
continue
}
if len(results) > 0 {
break
}
2024-06-10 09:49:40 +00:00
}
return results
}
2024-06-10 09:49:40 +00:00
func selectImageEngine() imageEngine {
imageEngineLock.Lock()
defer imageEngineLock.Unlock()
totalWeight := 0
for _, engine := range imageEngines {
totalWeight += engine.Weight
}
randValue := rand.Intn(totalWeight)
for _, engine := range imageEngines {
if randValue < engine.Weight {
// Adjust weights for load balancing
for i := range imageEngines {
if imageEngines[i].Name == engine.Name {
imageEngines[i].Weight = max(1, imageEngines[i].Weight-1)
} else {
imageEngines[i].Weight++
}
}
return engine
}
randValue -= engine.Weight
}
return imageEngines[0] // fallback to the first engine
}