efoot / app.py
Reaperxxxx's picture
Create app.py
ff98ad0 verified
Raw
History Blame Contribute Delete
12.6 kB
from flask import Flask, send_file, request
from PIL import Image, ImageDraw, ImageFont
import requests
import io
import os
from urllib.request import urlopen
app = Flask(__name__)
def create_gradient_vertical(width, height, color_top, color_bottom):
"""Create a smooth vertical gradient"""
base = Image.new('RGB', (width, height), color_top)
for y in range(height):
r = int(color_top[0] + (color_bottom[0] - color_top[0]) * y / height)
g = int(color_top[1] + (color_bottom[1] - color_top[1]) * y / height)
b = int(color_top[2] + (color_bottom[2] - color_top[2]) * y / height)
for x in range(width):
base.putpixel((x, y), (r, g, b))
return base
def get_font(size):
"""Try to load Comic Sans, fallback to others"""
font_paths = [
"comic.ttf",
"C:/Windows/Fonts/comic.ttf",
"/System/Library/Fonts/Supplemental/Comic Sans MS.ttf",
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
"arial.ttf",
"C:/Windows/Fonts/arial.ttf"
]
for path in font_paths:
try:
return ImageFont.truetype(path, size)
except:
continue
return ImageFont.load_default()
@app.route('/design')
def generate_card():
player_id = request.args.get('id', '89133456301399')
# Fetch player data
try:
response = requests.get(f'https://efootdb.vercel.app/api/playerinfo?id={player_id}')
data = response.json()
if not data.get('success'):
return "Player not found", 404
info = data['info']
except Exception as e:
return f"Error fetching data: {str(e)}", 500
# Card dimensions (16:9 ratio)
W, H = 1920, 1080
# Dark blue theme colors
DARK_NAVY = (10, 15, 35)
DEEP_BLUE = (15, 25, 55)
MEDIUM_BLUE = (25, 45, 85)
ACCENT_BLUE = (60, 100, 180)
LIGHT_BLUE = (100, 150, 220)
ELECTRIC_BLUE = (80, 180, 255)
WHITE = (255, 255, 255)
LIGHT_GRAY = (200, 210, 230)
GOLD = (255, 215, 0)
GREEN = (80, 220, 120)
RED = (255, 80, 80)
ORANGE = (255, 160, 60)
# Create base with gradient
img = create_gradient_vertical(W, H, DARK_NAVY, DEEP_BLUE)
# Try to load background if exists
try:
if os.path.exists('background.png'):
bg = Image.open('background.png').convert('RGB')
bg = bg.resize((W, H))
# Blend background with gradient (50% opacity)
img = Image.blend(img, bg, 0.3)
except:
pass
draw = ImageDraw.Draw(img)
# Load fonts
font_title = get_font(70)
font_name = get_font(90)
font_header = get_font(50)
font_large = get_font(42)
font_medium = get_font(36)
font_small = get_font(28)
font_tiny = get_font(24)
# === LEFT PANEL: Player Image & Basic Info ===
left_panel_x = 60
left_panel_y = 80
left_panel_w = 520
left_panel_h = 920
# Panel background
draw.rounded_rectangle(
[left_panel_x, left_panel_y, left_panel_x + left_panel_w, left_panel_y + left_panel_h],
radius=20,
fill=MEDIUM_BLUE
)
# Player image container
img_container_y = left_panel_y + 30
img_container_h = 480
draw.rounded_rectangle(
[left_panel_x + 30, img_container_y, left_panel_x + left_panel_w - 30, img_container_y + img_container_h],
radius=15,
fill=DEEP_BLUE
)
# Load and display player image
try:
player_img_response = urlopen(info['imageFront'])
player_img = Image.open(player_img_response).convert('RGBA')
# Calculate aspect ratio fit
max_w = left_panel_w - 80
max_h = img_container_h - 40
ratio = min(max_w / player_img.width, max_h / player_img.height)
new_w = int(player_img.width * ratio)
new_h = int(player_img.height * ratio)
player_img = player_img.resize((new_w, new_h), Image.Resampling.LANCZOS)
# Center the image
paste_x = left_panel_x + (left_panel_w - new_w) // 2
paste_y = img_container_y + (img_container_h - new_h) // 2
# Convert img to RGBA for pasting
img_rgba = img.convert('RGBA')
img_rgba.paste(player_img, (paste_x, paste_y), player_img)
img = img_rgba.convert('RGB')
draw = ImageDraw.Draw(img)
except:
pass
# Rating badge (circular)
badge_y = img_container_y + img_container_h + 30
badge_center_x = left_panel_x + left_panel_w // 2
badge_radius = 70
draw.ellipse(
[badge_center_x - badge_radius, badge_y - badge_radius,
badge_center_x + badge_radius, badge_y + badge_radius],
fill=RED,
outline=GOLD,
width=6
)
# Rating text
rating = info['attributes'][0]['value']
bbox = draw.textbbox((0, 0), rating, font=font_name)
text_w = bbox[2] - bbox[0]
text_h = bbox[3] - bbox[1]
draw.text(
(badge_center_x - text_w // 2, badge_y - text_h // 2 - 5),
rating,
font=font_name,
fill=WHITE
)
# Position and rarity
info_y = badge_y + badge_radius + 40
position_text = f"{info['position']}{info['rarity']}"
bbox = draw.textbbox((0, 0), position_text, font=font_large)
text_w = bbox[2] - bbox[0]
draw.text(
(badge_center_x - text_w // 2, info_y),
position_text,
font=font_large,
fill=GOLD
)
# Playing style
style_y = info_y + 50
style_text = info['playingStyle']
bbox = draw.textbbox((0, 0), style_text, font=font_medium)
text_w = bbox[2] - bbox[0]
draw.text(
(badge_center_x - text_w // 2, style_y),
style_text,
font=font_medium,
fill=LIGHT_BLUE
)
# Physical stats
phys_y = style_y + 70
phys_stats = [
f"Height: {info['height']}cm",
f"Weight: {info['weight']}kg",
f"Age: {info['age']}",
f"Foot: {info['foot']}"
]
for stat in phys_stats:
bbox = draw.textbbox((0, 0), stat, font=font_small)
text_w = bbox[2] - bbox[0]
draw.text(
(badge_center_x - text_w // 2, phys_y),
stat,
font=font_small,
fill=LIGHT_GRAY
)
phys_y += 38
# === TOP RIGHT: Header Section ===
header_x = left_panel_x + left_panel_w + 60
header_y = 80
header_w = W - header_x - 60
header_h = 180
draw.rounded_rectangle(
[header_x, header_y, header_x + header_w, header_y + header_h],
radius=20,
fill=MEDIUM_BLUE
)
# Title
draw.text((header_x + 40, header_y + 25), "PLAYER PROFILE", font=font_title, fill=ELECTRIC_BLUE)
# Player name
draw.text((header_x + 40, header_y + 100), info['playername'], font=font_name, fill=WHITE)
# === MIDDLE RIGHT: Team & Info ===
team_y = header_y + header_h + 30
team_h = 160
draw.rounded_rectangle(
[header_x, team_y, header_x + header_w, team_y + team_h],
radius=20,
fill=MEDIUM_BLUE
)
# Team info
team_info_y = team_y + 30
team_items = [
("Team:", info['teamname']),
("League:", info['league']),
("Nation:", f"{info['nationality']} ({info['region']})")
]
for label, value in team_items:
draw.text((header_x + 40, team_info_y), label, font=font_medium, fill=LIGHT_BLUE)
draw.text((header_x + 230, team_info_y), value, font=font_medium, fill=WHITE)
team_info_y += 42
# Squad number badge
squad_badge_x = header_x + header_w - 120
squad_badge_y = team_y + 30
squad_size = 100
draw.rounded_rectangle(
[squad_badge_x, squad_badge_y, squad_badge_x + squad_size, squad_badge_y + squad_size],
radius=15,
fill=DEEP_BLUE,
outline=GOLD,
width=4
)
squad_num = info['squadnumber']
bbox = draw.textbbox((0, 0), squad_num, font=font_name)
text_w = bbox[2] - bbox[0]
text_h = bbox[3] - bbox[1]
draw.text(
(squad_badge_x + (squad_size - text_w) // 2, squad_badge_y + (squad_size - text_h) // 2 - 5),
squad_num,
font=font_name,
fill=GOLD
)
# === BOTTOM RIGHT: Stats Section ===
stats_y = team_y + team_h + 30
stats_h = H - stats_y - 80
draw.rounded_rectangle(
[header_x, stats_y, header_x + header_w, stats_y + stats_h],
radius=20,
fill=MEDIUM_BLUE
)
# Stats header
draw.text((header_x + 40, stats_y + 25), "KEY ATTRIBUTES", font=font_header, fill=ELECTRIC_BLUE)
# Key stats to display (top performance stats)
key_stats = [
('Offensive Awareness', info['attributes'][1]['value'], info['attributes'][1].get('booster')),
('Dribbling', info['attributes'][3]['value'], info['attributes'][3].get('booster')),
('Tight Possession', info['attributes'][4]['value'], info['attributes'][4].get('booster')),
('Ball Control', info['attributes'][2]['value'], info['attributes'][2].get('booster')),
('Acceleration', info['attributes'][21]['value'], info['attributes'][21].get('booster')),
('Speed', info['attributes'][20]['value'], info['attributes'][20].get('booster')),
('Finishing', info['attributes'][7]['value'], info['attributes'][7].get('booster')),
('Balance', info['attributes'][25]['value'], info['attributes'][25].get('booster')),
]
stat_y = stats_y + 90
for stat_name, value, booster in key_stats:
# Stat name
draw.text((header_x + 40, stat_y), stat_name, font=font_medium, fill=LIGHT_GRAY)
# Progress bar background
bar_x = header_x + 380
bar_w = 500
bar_h = 28
draw.rounded_rectangle(
[bar_x, stat_y + 5, bar_x + bar_w, stat_y + 5 + bar_h],
radius=14,
fill=DEEP_BLUE
)
# Progress bar fill
try:
val_int = int(value)
filled_w = int((val_int / 99) * bar_w)
# Color based on value
if val_int >= 90:
bar_color = GREEN
elif val_int >= 80:
bar_color = LIGHT_BLUE
elif val_int >= 70:
bar_color = ORANGE
else:
bar_color = ACCENT_BLUE
if filled_w > 0:
draw.rounded_rectangle(
[bar_x, stat_y + 5, bar_x + filled_w, stat_y + 5 + bar_h],
radius=14,
fill=bar_color
)
except:
pass
# Value text
value_text = value
if booster:
value_text = f"{value} {booster}"
draw.text((bar_x + bar_w + 25, stat_y + 2), value_text, font=font_large, fill=WHITE)
stat_y += 55
# Skills section
skills_x = header_x + 40
skills_y = stat_y + 20
draw.text((skills_x, skills_y), "Special Skills:", font=font_medium, fill=LIGHT_BLUE)
skills_y += 40
skills = info.get('playerSkills', [])
skill_text = " • ".join(skills[:6]) # Top 6 skills
# Word wrap for skills
words = skill_text.split()
lines = []
current_line = []
for word in words:
test_line = ' '.join(current_line + [word])
bbox = draw.textbbox((0, 0), test_line, font=font_small)
if bbox[2] - bbox[0] <= header_w - 100:
current_line.append(word)
else:
if current_line:
lines.append(' '.join(current_line))
current_line = [word]
if current_line:
lines.append(' '.join(current_line))
for line in lines[:2]: # Max 2 lines
draw.text((skills_x, skills_y), line, font=font_small, fill=LIGHT_GRAY)
skills_y += 32
# === FOOTER ===
footer_y = H - 50
draw.text((60, footer_y), f"ID: {player_id}", font=font_tiny, fill=LIGHT_GRAY)
draw.text((W - 350, footer_y), f"Max Level: {info['maximumlevel']} • Form: {info['attributes'][28]['value']}", font=font_tiny, fill=LIGHT_GRAY)
# Save to bytes
img_io = io.BytesIO()
img.save(img_io, 'PNG', quality=95)
img_io.seek(0)
return send_file(img_io, mimetype='image/png')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)