import os import requests import gradio as gr from datetime import datetime, timedelta # ============================================================ # CONFIGURATION # ============================================================ API_KEY = os.getenv("FOOTBALL_API_KEY") BASE_URL = "https://v3.football.api-sports.io" HEADERS = { "x-apisports-key": API_KEY or "" } LEAGUES = { "Premier League": 39, "La Liga": 140, "Serie A": 135, "Bundesliga": 78, "Ligue 1": 61, "Champions League": 2, "Europa League": 3, "World Cup": 1, "Euro Championship": 4, } # ============================================================ # API HELPER # ============================================================ def api_get(endpoint, params=None): if not API_KEY: return { "error": "API key missing. Add FOOTBALL_API_KEY in Hugging Face Secrets." } try: response = requests.get( BASE_URL + endpoint, headers=HEADERS, params=params or {}, timeout=20 ) if response.status_code != 200: return { "error": f"API error: HTTP {response.status_code}" } return response.json() except requests.exceptions.Timeout: return {"error": "Request timed out."} except Exception as e: return {"error": str(e)} # ============================================================ # COMMON HTML # ============================================================ def error_box(message): return f"""
⚠️ {message}
""" def loading_box(): return """
⚽ Loading football data...
""" def team_logo(url, size=45): if not url: return "" return f'' # ============================================================ # FIXTURES # ============================================================ def get_fixtures(league_name, date): if not date: date = datetime.now().strftime("%Y-%m-%d") league_id = LEAGUES.get(league_name) if not league_id: return error_box("League not found.") data = api_get( "/fixtures", { "league": league_id, "date": date } ) if "error" in data: return error_box(data["error"]) matches = data.get("response", []) if not matches: return f"""
⚽ No matches found for {league_name} on {date}.
""" html = f"""
⚽ {league_name} {date}
""" for match in matches: fixture = match["fixture"] teams = match["teams"] goals = match["goals"] home = teams["home"] away = teams["away"] status = fixture["status"]["short"] home_score = goals["home"] away_score = goals["away"] if home_score is None: score = "VS" else: score = f"{home_score} - {away_score}" try: dt = datetime.fromisoformat( fixture["date"].replace("Z", "+00:00") ) match_time = dt.strftime("%H:%M") except: match_time = fixture["date"] html += f"""
{status} {match_time}
{team_logo(home.get("logo"))} {home.get("name", "")}
{score}
{team_logo(away.get("logo"))} {away.get("name", "")}
🏟️ {fixture.get("venue", {}).get("name") or "Venue unavailable"}
""" return html # ============================================================ # LIVE MATCHES # ============================================================ def get_live_matches(): data = api_get( "/fixtures", { "live": "all" } ) if "error" in data: return error_box(data["error"]) matches = data.get("response", []) if not matches: return """
🔴 No live matches right now.
""" html = """
🔴 LIVE MATCHES
""" for match in matches: teams = match["teams"] goals = match["goals"] fixture = match["fixture"] home = teams["home"] away = teams["away"] hs = goals["home"] if goals["home"] is not None else 0 aws = goals["away"] if goals["away"] is not None else 0 elapsed = fixture["status"].get("elapsed") html += f"""
🔴 LIVE {f" · {elapsed}'" if elapsed else ""}
{team_logo(home.get("logo"))} {home.get("name")}
{hs} - {aws}
{team_logo(away.get("logo"))} {away.get("name")}
""" return html # ============================================================ # STANDINGS # ============================================================ def get_standings(league_name): league_id = LEAGUES.get(league_name) if not league_id: return error_box("League not found.") season = datetime.now().year # Football seasons span two years. if datetime.now().month < 7: season -= 1 data = api_get( "/standings", { "league": league_id, "season": season } ) if "error" in data: return error_box(data["error"]) response = data.get("response", []) if not response: return """
No standings available.
""" standings = response[0]["league"]["standings"][0] html = f"""
🏆 {league_name} Standings
""" for row in standings: team = row["team"] html += f""" """ html += """
# Team P W D L GD PTS
{row["rank"]} {team_logo(team.get("logo"), 30)} {team.get("name")} {row["all"]["played"]} {row["all"]["win"]} {row["all"]["draw"]} {row["all"]["lose"]} {row["goalsDiff"]} {row["points"]}
""" return html # ============================================================ # TEAM SEARCH # ============================================================ def search_team(team_name): if not team_name or not team_name.strip(): return """
Enter a team name.
""" data = api_get( "/teams", { "search": team_name } ) if "error" in data: return error_box(data["error"]) teams = data.get("response", []) if not teams: return """
❌ Team not found.
""" html = """
🔎 Teams
""" for item in teams[:10]: team = item["team"] html += f"""
{team_logo(team.get("logo"), 60)}

{team.get("name")}

Country: {team.get("country")}

Founded: {team.get("founded") or "N/A"}

""" return html # ============================================================ # TODAY # ============================================================ def today_matches(): today = datetime.now().strftime("%Y-%m-%d") html = """
📅 Today's Matches
""" leagues_to_check = [ ("Premier League", 39), ("La Liga", 140), ("Serie A", 135), ("Bundesliga", 78), ("Ligue 1", 61), ] found = False for league_name, league_id in leagues_to_check: data = api_get( "/fixtures", { "league": league_id, "date": today } ) if "error" in data: return error_box(data["error"]) matches = data.get("response", []) if not matches: continue found = True html += f"""

{league_name}

""" for match in matches: teams = match["teams"] goals = match["goals"] home = teams["home"] away = teams["away"] hs = goals["home"] aws = goals["away"] if hs is None: score = "VS" else: score = f"{hs} - {aws}" html += f"""
{team_logo(home.get("logo"), 35)} {home.get("name")}
{score}
{team_logo(away.get("logo"), 35)} {away.get("name")}
""" if not found: return """
No major-league matches today.
""" return html # ============================================================ # HOME # ============================================================ def home_page(): today = datetime.now().strftime("%Y-%m-%d") return f"""
⚽

Football Live

Live scores · Fixtures · Standings · Teams

📅 {today}
🔴

Live Scores

Follow live football matches.

📅

Fixtures

Check upcoming matches.

🏆

Standings

View league tables.

🔎

Teams

Search football clubs.

""" # ============================================================ # CSS # ============================================================ CSS = """ body { background: #0b1220 !important; } .gradio-container { max-width: 1100px !important; margin: auto !important; } .hero { text-align: center; padding: 45px 20px; border-radius: 25px; background: linear-gradient( 135deg, #111827, #172554 ); color: white; margin-bottom: 25px; } .hero h1 { font-size: 42px; margin: 10px; } .hero p { font-size: 18px; opacity: .8; } .hero-ball { font-size: 65px; } .hero-date { margin-top: 20px; opacity: .7; } .feature-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; } .feature { padding: 25px; background: #111827; color: white; border-radius: 18px; text-align: center; border: 1px solid #263244; } .feature p { color: #9ca3af; } .match-card { background: #111827; color: white; border-radius: 18px; padding: 20px; margin: 15px 0; border: 1px solid #263244; } .match-header { display: flex; justify-content: space-between; color: #9ca3af; font-size: 13px; margin-bottom: 20px; } .teams { display: flex; justify-content: space-around; align-items: center; text-align: center; } .team { width: 35%; display: flex; flex-direction: column; align-items: center; gap: 8px; } .logo { object-fit: contain; } .score { font-size: 27px; font-weight: bold; width: 20%; } .venue { text-align: center; color: #9ca3af; margin-top: 18px; font-size: 13px; } .live-card { border-left: 4px solid #ef4444; } .live-status { color: #ef4444; font-weight: bold; margin-bottom: 15px; } .section-title { font-size: 24px; font-weight: bold; color: white; margin: 20px 0; } .section-title span { float: right; font-size: 14px; color: #9ca3af; } .empty, .error-box, .loading { padding: 35px; text-align: center; background: #111827; color: white; border-radius: 18px; margin: 15px 0; } .error-box { color: #fca5a5; } .table-wrapper { overflow-x: auto; } table { width: 100%; border-collapse: collapse; background: #111827; color: white; border-radius: 15px; overflow: hidden; } th { background: #1f2937; padding: 14px; text-align: center; } td { padding: 12px; text-align: center; border-bottom: 1px solid #263244; } .club { display: flex; align-items: center; gap: 10px; text-align: left; } .team-result { display: flex; align-items: center; gap: 20px; background: #111827; color: white; padding: 20px; margin: 12px 0; border-radius: 18px; } .team-result h3 { margin: 0; } .team-result p { color: #9ca3af; margin: 5px 0; } .small-match { display: flex; justify-content: space-between; align-items: center; background: #111827; color: white; padding: 15px; margin: 8px 0; border-radius: 14px; } .small-match > div { display: flex; align-items: center; gap: 8px; } @media(max-width: 700px) { .feature-grid { grid-template-columns: repeat(2, 1fr); } .hero h1 { font-size: 32px; } .team { font-size: 13px; } .score { font-size: 20px; } } """ # ============================================================ # GRADIO APP # ============================================================ with gr.Blocks( title="Football Live", css=CSS, theme=gr.themes.Soft() ) as app: gr.Markdown( """ # ⚽ FOOTBALL LIVE **Live football scores, fixtures, standings and teams** """ ) # -------------------------------------------------------- # HOME # -------------------------------------------------------- with gr.Tab("🏠 Home"): home_output = gr.HTML( value=home_page() ) home_refresh = gr.Button( "🔄 Refresh" ) home_refresh.click( fn=home_page, outputs=home_output ) # -------------------------------------------------------- # LIVE # -------------------------------------------------------- with gr.Tab("🔴 Live"): live_output = gr.HTML( value="
Press Refresh to load live matches.
" ) live_button = gr.Button( "🔴 Load Live Matches", variant="primary" ) live_button.click( fn=get_live_matches, outputs=live_output ) # -------------------------------------------------------- # FIXTURES # -------------------------------------------------------- with gr.Tab("📅 Fixtures"): with gr.Row(): fixture_league = gr.Dropdown( choices=list(LEAGUES.keys()), value="Premier League", label="League" ) fixture_date = gr.Textbox( value=datetime.now().strftime("%Y-%m-%d"), label="Date", placeholder="YYYY-MM-DD" ) fixture_button = gr.Button( "🔎 Load Fixtures", variant="primary" ) fixture_output = gr.HTML() fixture_button.click( fn=get_fixtures, inputs=[ fixture_league, fixture_date ], outputs=fixture_output ) # -------------------------------------------------------- # STANDINGS # -------------------------------------------------------- with gr.Tab("🏆 Standings"): standings_league = gr.Dropdown( choices=list(LEAGUES.keys()), value="Premier League", label="League" ) standings_button = gr.Button( "🏆 Load Standings", variant="primary" ) standings_output = gr.HTML() standings_button.click( fn=get_standings, inputs=standings_league, outputs=standings_output ) # -------------------------------------------------------- # TEAMS # -------------------------------------------------------- with gr.Tab("🔎 Teams"): team_input = gr.Textbox( label="Team name", placeholder="e.g. Arsenal" ) team_button = gr.Button( "🔎 Search Team", variant="primary" ) team_output = gr.HTML() team_button.click( fn=search_team, inputs=team_input, outputs=team_output ) # -------------------------------------------------------- # TODAY # -------------------------------------------------------- with gr.Tab("📅 Today"): today_output = gr.HTML( value="
Press the button to load today's matches.
" ) today_button = gr.Button( "📅 Load Today's Matches", variant="primary" ) today_button.click( fn=today_matches, outputs=today_output ) gr.Markdown( """ --- ### ⚽ Football Live Data provided through API-Football. """ ) # ============================================================ # START # ============================================================ if __name__ == "__main__": app.launch()