package main import ( "fmt" "html/template" "log" "math/rand" "net/http" "sync" "time" ) var ( imageEngines []imageEngine imageEngineLock sync.Mutex ) type imageEngine struct { Name string Func func(string, string, string, int) ([]ImageSearchResult, error) Weight int } func init() { imageEngines = []imageEngine{ {Name: "Qwant", Func: PerformQwantImageSearch, Weight: 1}, {Name: "Imgur", Func: PerformImgurImageSearch, Weight: 2}, } rand.Seed(time.Now().UnixNano()) } 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) 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 { Results []ImageSearchResult Query string Page int Fetched string LanguageOptions []LanguageOption CurrentLang string HasPrevPage bool HasNextPage bool }{ Results: combinedResults, Query: query, Page: page, Fetched: fmt.Sprintf("%.2f seconds", elapsedTime.Seconds()), 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 { combinedResults = fetchImageResults(query, safe, lang, page) resultsCache.Set(cacheKey, convertToSearchResults(combinedResults)) } else { _, _, imageResults := convertToSpecificResults(results) combinedResults = imageResults } case <-time.After(2 * time.Second): log.Println("Cache check timeout") combinedResults = fetchImageResults(query, safe, lang, page) resultsCache.Set(cacheKey, convertToSearchResults(combinedResults)) } return combinedResults } func fetchImageResults(query, safe, lang string, page int) []ImageSearchResult { 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) return nil } return results } 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 }