mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-05 11:12:56 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d166e8bd2a | |||
| 1077d518fe |
@@ -17,7 +17,6 @@ import (
|
|||||||
"fotbal-club/internal/models"
|
"fotbal-club/internal/models"
|
||||||
"fotbal-club/internal/services"
|
"fotbal-club/internal/services"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -224,102 +223,53 @@ func (fc *FACRController) SearchClubs(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use external FACR API proxy instead of scraping www.fotbal.cz directly
|
||||||
vals := neturl.Values{}
|
vals := neturl.Values{}
|
||||||
vals.Set("q", q)
|
vals.Set("q", q)
|
||||||
searchURL := "https://www.fotbal.cz/club/hledej?" + vals.Encode()
|
searchURL := "https://facr.tdvorak.dev/club/search?" + vals.Encode()
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", searchURL, nil)
|
httpClient := &http.Client{Timeout: 15 * time.Second}
|
||||||
|
resp, err := httpClient.Get(searchURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error creating request: %v", err)})
|
c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Error fetching from FACR API: %v", err)})
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36")
|
|
||||||
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8")
|
|
||||||
req.Header.Set("Accept-Language", "cs-CZ,cs;q=0.9,en;q=0.8")
|
|
||||||
req.Header.Set("Referer", "https://www.fotbal.cz/club/hledej")
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error fetching search page: %v", err)})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
// Try once more with quoted query if short tokens present
|
body, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
||||||
resp.Body.Close()
|
c.Data(resp.StatusCode, "application/json", body)
|
||||||
searchURL2 := searchURL
|
|
||||||
tokens := strings.Fields(q)
|
|
||||||
for _, t := range tokens {
|
|
||||||
if len([]rune(t)) <= 2 {
|
|
||||||
vals2 := neturl.Values{}
|
|
||||||
vals2.Set("q", "\""+q+"\"")
|
|
||||||
searchURL2 = "https://www.fotbal.cz/club/hledej?" + vals2.Encode()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req2, _ := http.NewRequest("GET", searchURL2, nil)
|
|
||||||
req2.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36")
|
|
||||||
req2.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
|
||||||
req2.Header.Set("Accept-Language", "en-US,en;q=0.9")
|
|
||||||
resp2, err2 := client.Do(req2)
|
|
||||||
if err2 != nil {
|
|
||||||
c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Error fetching (retry): %v", err2)})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp2.Body.Close()
|
|
||||||
if resp2.StatusCode != http.StatusOK {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"query": q, "count": 0, "results": []SearchResult{}})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp = resp2
|
|
||||||
}
|
|
||||||
|
|
||||||
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error parsing HTML: %v", err)})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var results []SearchResult
|
|
||||||
doc.Find("li.ListItemSplit").Each(func(_ int, li *goquery.Selection) {
|
// Read and parse the external API response
|
||||||
a := li.Find("a.Link--inverted").First()
|
body, err := io.ReadAll(resp.Body)
|
||||||
href, _ := a.Attr("href")
|
if err != nil {
|
||||||
if href == "" {
|
c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Error reading response: %v", err)})
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the JSON to apply local processing (logo handling, filtering, deduplication)
|
||||||
|
var apiResp struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
Results []SearchResult `json:"results"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error parsing response: %v", err)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
results := apiResp.Results
|
||||||
|
|
||||||
|
// Process logos through local rembg service
|
||||||
|
for i := range results {
|
||||||
|
if logoURL := strings.TrimSpace(results[i].LogoURL); logoURL != "" {
|
||||||
|
if p, err := services.ProcessFACRLogo(logoURL); err == nil && strings.TrimSpace(p) != "" {
|
||||||
|
results[i].LogoURL = p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
name := strings.TrimSpace(a.Find("span.H7").First().Text())
|
}
|
||||||
if name == "" {
|
|
||||||
name = strings.TrimSpace(a.Text())
|
|
||||||
}
|
|
||||||
img := a.Find("img").First()
|
|
||||||
logoURL, _ := img.Attr("src")
|
|
||||||
// Best-effort: Process FACR logos to transparent PNG. Non-facr URLs are returned unchanged.
|
|
||||||
if p, err := services.ProcessFACRLogo(logoURL); err == nil && strings.TrimSpace(p) != "" {
|
|
||||||
logoURL = p
|
|
||||||
}
|
|
||||||
category := strings.TrimSpace(li.Find(".ClubCategories .BadgeCategory").First().Text())
|
|
||||||
address := strings.TrimSpace(li.Find(".ClubAddress p").First().Text())
|
|
||||||
clubType := "football"
|
|
||||||
if strings.Contains(strings.ToLower(href), "/futsal/") {
|
|
||||||
clubType = "futsal"
|
|
||||||
}
|
|
||||||
parts := strings.Split(strings.TrimRight(href, "/"), "/")
|
|
||||||
clubID := ""
|
|
||||||
if len(parts) > 0 {
|
|
||||||
clubID = parts[len(parts)-1]
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(href, "http://") && !strings.HasPrefix(href, "https://") {
|
|
||||||
href = "https://www.fotbal.cz" + href
|
|
||||||
}
|
|
||||||
results = append(results, SearchResult{
|
|
||||||
Name: name,
|
|
||||||
ClubID: clubID,
|
|
||||||
ClubType: clubType,
|
|
||||||
URL: href,
|
|
||||||
LogoURL: logoURL,
|
|
||||||
Category: category,
|
|
||||||
Address: address,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// If setup is completed and a sport is configured, filter results to that sport only
|
// If setup is completed and a sport is configured, filter results to that sport only
|
||||||
if fc.DB != nil {
|
if fc.DB != nil {
|
||||||
@@ -338,6 +288,7 @@ func (fc *FACRController) SearchClubs(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deduplicate results
|
||||||
if len(results) > 1 {
|
if len(results) > 1 {
|
||||||
unique := make([]SearchResult, 0, len(results))
|
unique := make([]SearchResult, 0, len(results))
|
||||||
seenByID := map[string]int{}
|
seenByID := map[string]int{}
|
||||||
@@ -379,7 +330,6 @@ func (fc *FACRController) SearchClubs(c *gin.Context) {
|
|||||||
results = unique
|
results = unique
|
||||||
}
|
}
|
||||||
|
|
||||||
// respond and close the function
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"query": q,
|
"query": q,
|
||||||
"count": len(results),
|
"count": len(results),
|
||||||
|
|||||||
Reference in New Issue
Block a user