package main import ( "encoding/json" "fmt" "net/http" "net/url" "sync" "time" ) // QwantAPIResponse represents the JSON response structure from Qwant API type QwantAPIResponse struct { Data struct { Result struct { Items []struct { Media string `json:"media"` Thumbnail string `json:"thumbnail"` Title string `json:"title"` Url string `json:"url"` Width int `json:"width"` Height int `json:"height"` } `json:"items"` } `json:"result"` } `json:"data"` } func ConvertToQwantLocale(langCode string) string { langMap := map[string]string{ "en": "en_us", // English "af": "en_us", // Afrikaans (no direct match, default to English) "ar": "ar", // Arabic "hy": "en_us", // Armenian (no direct match, default to English) "be": "en_us", // Belarusian (no direct match, default to English) "bg": "bg_bg", // Bulgarian "ca": "ca_es", // Catalan "zh-CN": "zh_cn", // Chinese Simplified "zh-TW": "zh_hk", // Chinese Traditional "hr": "en_us", // Croatian (no direct match, default to English) "cs": "cs_cz", // Czech "da": "da_dk", // Danish "nl": "nl_nl", // Dutch "eo": "en_us", // Esperanto (no direct match, default to English) "et": "et_ee", // Estonian "tl": "en_us", // Tagalog (no direct match, default to English) "fi": "fi_fi", // Finnish "fr": "fr_fr", // French "de": "de_de", // German "el": "el_gr", // Greek "iw": "he_il", // Hebrew "hi": "en_us", // Hindi (no direct match, default to English) "hu": "hu_hu", // Hungarian "is": "en_us", // Icelandic (no direct match, default to English) "id": "en_us", // Indonesian (no direct match, default to English) "it": "it_it", // Italian "ja": "ja_jp", // Japanese "ko": "ko_kr", // Korean "lv": "en_us", // Latvian (no direct match, default to English) "lt": "en_us", // Lithuanian (no direct match, default to English) "no": "nb_no", // Norwegian "fa": "en_us", // Persian (no direct match, default to English) "pl": "pl_pl", // Polish "pt": "pt_pt", // Portuguese "ro": "ro_ro", // Romanian "ru": "ru_ru", // Russian "sr": "en_us", // Serbian (no direct match, default to English) "sk": "en_us", // Slovak (no direct match, default to English) "sl": "en_us", // Slovenian (no direct match, default to English) "es": "es_es", // Spanish "sw": "en_us", // Swahili (no direct match, default to English) "sv": "sv_se", // Swedish "th": "th_th", // Thai "tr": "tr_tr", // Turkish "uk": "en_us", // Ukrainian (no direct match, default to English) "vi": "en_us", // Vietnamese (no direct match, default to English) "": "en_us", // Default to English if no language is provided } if qwantLocale, exists := langMap[langCode]; exists { return qwantLocale } printWarn("Qwant locale code missing: %v, defaulting to: en_us", langCode) return "en_us" // Default fallback } // PerformQwantImageSearch performs an image search on Qwant and returns the results. func PerformQwantImageSearch(query, safe, lang string, page int) ([]ImageSearchResult, time.Duration, error) { startTime := time.Now() // Start the timer const resultsPerPage = 50 var offset int if page <= 1 { offset = 0 } else { offset = (page - 1) * resultsPerPage } // Ensure count + offset is within acceptable limits if offset+resultsPerPage > 250 { return nil, 0, fmt.Errorf("count + offset must be lower than 250 for quant") } if safe == "" { safe = "0" } lang = ConvertToQwantLocale(lang) apiURL := fmt.Sprintf("https://api.qwant.com/v3/search/images?t=images&q=%s&count=%d&locale=%s&offset=%d&device=desktop&tgp=2&safesearch=%s", url.QueryEscape(query), resultsPerPage, lang, offset, safe) client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", apiURL, nil) if err != nil { return nil, 0, fmt.Errorf("creating request: %v", err) } ImageUserAgent, err := GetUserAgent("Image-Search-Quant") if err != nil { return nil, 0, err } req.Header.Set("User-Agent", ImageUserAgent) resp, err := client.Do(req) if err != nil { return nil, 0, fmt.Errorf("making request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, 0, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var apiResp QwantAPIResponse if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { return nil, 0, fmt.Errorf("decoding response: %v", err) } var wg sync.WaitGroup results := make([]ImageSearchResult, len(apiResp.Data.Result.Items)) for i, item := range apiResp.Data.Result.Items { wg.Add(1) go func(i int, item struct { Media string `json:"media"` Thumbnail string `json:"thumbnail"` Title string `json:"title"` Url string `json:"url"` Width int `json:"width"` Height int `json:"height"` }) { defer wg.Done() // Populate the result results[i] = ImageSearchResult{ Thumbnail: item.Thumbnail, Title: item.Title, Media: item.Media, Source: item.Url, ThumbProxy: "/imgproxy?url=" + item.Media, Width: item.Width, Height: item.Height, } }(i, item) } // Wait for all goroutines to complete wg.Wait() duration := time.Since(startTime) // Calculate the duration return results, duration, nil }