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"""
{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
| # |
Team |
P |
W |
D |
L |
GD |
PTS |
"""
for row in standings:
team = row["team"]
html += f"""
| {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"]}
|
"""
html += """
"""
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()