@@ -458,15 +458,37 @@ func getParentLeagueID(leagueName string, leagueID int) int {
458458
459459// LeagueTable retrieves the league table/standings for a specific league.
460460// Handles both regular league tables and knockout competition tables (e.g., Champions League).
461- // Uses league name to detect parent leagues for knockout competitions.
461+ // Uses parentLeagueID (from FotMob match details) when available, then falls back to
462+ // league name pattern matching for knockout competitions.
463+ // Multi-season leagues (e.g., Liga MX, Liga Profesional) have sub-league IDs per season
464+ // that don't have standings — the parentLeagueID points to the main league that does.
462465func (c * Client ) LeagueTable (ctx context.Context , leagueID int , leagueName string ) ([]api.LeagueTableEntry , error ) {
463- // First, determine the effective league ID (may be parent for knockout competitions)
466+ // Determine the effective league ID for standings lookup.
467+ // Priority: parentLeagueID from match details > name pattern matching > original ID
464468 effectiveID := getParentLeagueID (leagueName , leagueID )
465469
466470 // Fetch standings using the effective league ID
467471 return c .fetchLeagueTable (ctx , effectiveID )
468472}
469473
474+ // LeagueTableWithParent retrieves the league table/standings, using the parent league ID
475+ // when available. This is the preferred method when match details provide a parentLeagueId.
476+ // Multi-season leagues (e.g., Liga MX Clausura, Liga Profesional Apertura) return sub-league
477+ // IDs in match details that have no standings — the parentLeagueID points to the main league.
478+ func (c * Client ) LeagueTableWithParent (ctx context.Context , leagueID int , leagueName string , parentLeagueID int ) ([]api.LeagueTableEntry , error ) {
479+ effectiveID := leagueID
480+
481+ // Use parentLeagueID if it differs from leagueID (indicates a sub-season league)
482+ if parentLeagueID > 0 && parentLeagueID != leagueID {
483+ effectiveID = parentLeagueID
484+ } else {
485+ // Fall back to name-based parent league detection for knockout competitions
486+ effectiveID = getParentLeagueID (leagueName , leagueID )
487+ }
488+
489+ return c .fetchLeagueTable (ctx , effectiveID )
490+ }
491+
470492// fetchLeagueTable fetches the league table for a specific league ID.
471493func (c * Client ) fetchLeagueTable (ctx context.Context , leagueID int ) ([]api.LeagueTableEntry , error ) {
472494 // Apply rate limiting
@@ -491,21 +513,29 @@ func (c *Client) fetchLeagueTable(ctx context.Context, leagueID int) ([]api.Leag
491513 return nil , fmt .Errorf ("unexpected status code %d for league %d table" , resp .StatusCode , leagueID )
492514 }
493515
494- // FotMob returns table at either:
495- // - Regular leagues: table[0].data.table.all[]
496- // - Knockout competitions (e.g., Champions League): table[0].data.tables[0].table.all[]
516+ // FotMob returns table data in several formats:
517+ // 1. Regular leagues (EPL, La Liga): table[0].data.table.all[]
518+ // 2. Knockout competitions (Champions League): table[0].data.tables[0].table.all[]
519+ // 3. Multi-season leagues (Liga MX, Liga Profesional): table[0].data.tables[] with
520+ // multiple sub-tables (e.g., Clausura + Apertura, or Group A + Group B).
521+ // The first sub-table is typically the current/most relevant season.
497522 var response struct {
498523 Table []struct {
499524 Data struct {
500525 // Regular league table
501526 Table struct {
502527 All []fotmobTableRow `json:"all"`
503528 } `json:"table"`
504- // Knockout competition tables (e.g., Champions League)
529+ // Multi-table format: knockout competitions and multi-season leagues
530+ // Examples:
531+ // - Champions League: single table with all teams
532+ // - Liga MX: Clausura + Apertura tables
533+ // - Liga Profesional: Apertura Group A + Group B tables
505534 Tables []struct {
506535 Table struct {
507536 All []fotmobTableRow `json:"all"`
508537 } `json:"table"`
538+ LeagueName string `json:"leagueName"` // e.g., "Clausura", "Apertura - Group A"
509539 } `json:"tables"`
510540 } `json:"data"`
511541 } `json:"table"`
@@ -515,16 +545,22 @@ func (c *Client) fetchLeagueTable(ctx context.Context, leagueID int) ([]api.Leag
515545 return nil , fmt .Errorf ("decode league table response for league %d: %w" , leagueID , err )
516546 }
517547
518- // Extract table rows - try regular format first, then knockout format
548+ // Extract table rows - try regular format first, then multi-table format
519549 var tableData []fotmobTableRow
520550 if len (response .Table ) > 0 {
521551 data := response .Table [0 ].Data
522- // Try regular league format first
552+ // Try regular league format first (single table with all teams)
523553 if len (data .Table .All ) > 0 {
524554 tableData = data .Table .All
525- } else if len (data .Tables ) > 0 && len (data .Tables [0 ].Table .All ) > 0 {
526- // Fall back to knockout competition format
527- tableData = data .Tables [0 ].Table .All
555+ } else if len (data .Tables ) > 0 {
556+ // Multi-table format: use first sub-table (current/most relevant season)
557+ // This covers both knockout competitions and multi-season leagues
558+ for _ , subTable := range data .Tables {
559+ if len (subTable .Table .All ) > 0 {
560+ tableData = subTable .Table .All
561+ break
562+ }
563+ }
528564 }
529565 }
530566
0 commit comments