Files
MyClub/internal/services/facr_logos_batch.go
T
Tomas Dvorak f5b6f83974 dev day #99
2025-11-21 08:44:44 +01:00

182 lines
4.7 KiB
Go

package services
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"
"fotbal-club/internal/config"
)
// EnsureFACRLogosProcessed triggers post-processing of FACR logos referenced
// in cached prefetch JSON files (facr_club_info.json, facr_tables.json).
// It is idempotent and safe to call multiple times.
func EnsureFACRLogosProcessed(cacheDir string) error {
if config.AppConfig != nil && !config.AppConfig.RembgEnabled {
return nil
}
var firstErr error
clubInfo := filepath.Join(cacheDir, "facr_club_info.json")
if _, err := os.Stat(clubInfo); err == nil {
if err := postProcessFACRClubInfoLogos(cacheDir); err != nil && firstErr == nil {
firstErr = fmt.Errorf("club_info: %w", err)
}
}
tables := filepath.Join(cacheDir, "facr_tables.json")
if _, err := os.Stat(tables); err == nil {
if err := postProcessFACRTablesLogos(cacheDir); err != nil && firstErr == nil {
firstErr = fmt.Errorf("tables: %w", err)
}
}
return firstErr
}
// progress state for rembg batch
var rembgMu sync.RWMutex
var rembgRunning bool
var rembgTotal int
var rembgDone int
var rembgStarted time.Time
var rembgFinished *time.Time
// RembgStatus represents current background removal progress
type RembgStatus struct {
Running bool `json:"running"`
Total int `json:"total"`
Done int `json:"done"`
StartedAt time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
}
// GetRembgStatus returns a snapshot of current rembg batch progress
func GetRembgStatus() RembgStatus {
rembgMu.RLock()
defer rembgMu.RUnlock()
return RembgStatus{
Running: rembgRunning,
Total: rembgTotal,
Done: rembgDone,
StartedAt: rembgStarted,
FinishedAt: rembgFinished,
}
}
// StartFACRLogosBatch scans cached FACR JSONs and processes all referenced FACR logos
// using ProcessFACRLogo. Progress is tracked via GetRembgStatus. If a batch is already
// running, this call is a no-op and returns false. Otherwise returns true and starts
// a background goroutine.
func StartFACRLogosBatch(cacheDir string) bool {
if config.AppConfig != nil && !config.AppConfig.RembgEnabled {
return false
}
rembgMu.Lock()
if rembgRunning {
rembgMu.Unlock()
return false
}
// Collect unique FACR logo URLs to determine total
urls := collectFACRLogos(cacheDir)
rembgRunning = true
rembgTotal = len(urls)
rembgDone = 0
rembgStarted = time.Now()
rembgFinished = nil
rembgMu.Unlock()
if len(urls) == 0 {
// nothing to do; mark finished quickly
rembgMu.Lock()
rembgRunning = false
now := time.Now()
rembgFinished = &now
rembgMu.Unlock()
return true
}
go func(urlList []string) {
defer func() {
// Best-effort: after processing, ensure JSONs are rewritten to point to processed URLs
_ = EnsureFACRLogosProcessed(cacheDir)
rembgMu.Lock()
rembgRunning = false
now := time.Now()
rembgFinished = &now
rembgMu.Unlock()
}()
for _, u := range urlList {
_, _ = ProcessFACRLogo(u) // best effort; skip errors
rembgMu.Lock()
rembgDone++
rembgMu.Unlock()
}
}(urls)
return true
}
// collectFACRLogos returns unique FACR-hosted logo URLs from cached prefetch JSONs.
func collectFACRLogos(cacheDir string) []string {
uniq := map[string]struct{}{}
add := func(s string) {
s = strings.TrimSpace(s)
if s == "" {
return
}
// Only FACR sources; skip already-processed local /uploads and other hosts
ls := strings.ToLower(s)
if strings.HasPrefix(ls, "/uploads/") || strings.HasPrefix(ls, "/dist/") {
return
}
if !strings.Contains(ls, "fotbal.cz") {
return
}
uniq[s] = struct{}{}
}
// facr_club_info.json
if b, err := os.ReadFile(filepath.Join(cacheDir, "facr_club_info.json")); err == nil {
var payload struct {
Competitions []struct {
Matches []struct {
HomeLogoURL string `json:"home_logo_url"`
AwayLogoURL string `json:"away_logo_url"`
} `json:"matches"`
} `json:"competitions"`
}
if json.Unmarshal(b, &payload) == nil {
for _, c := range payload.Competitions {
for _, m := range c.Matches {
add(m.HomeLogoURL)
add(m.AwayLogoURL)
}
}
}
}
// facr_tables.json
if b, err := os.ReadFile(filepath.Join(cacheDir, "facr_tables.json")); err == nil {
var payload struct {
Competitions []struct {
Table struct {
Overall []struct {
TeamLogoURL string `json:"team_logo_url"`
} `json:"overall"`
} `json:"table"`
} `json:"competitions"`
}
if json.Unmarshal(b, &payload) == nil {
for _, c := range payload.Competitions {
for _, r := range c.Table.Overall {
add(r.TeamLogoURL)
}
}
}
}
out := make([]string, 0, len(uniq))
for k := range uniq {
out = append(out, k)
}
return out
}