Reaperxxxx commited on
Commit
ff98ad0
·
verified ·
1 Parent(s): 26a76ec

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +405 -0
app.py ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, send_file, request
2
+ from PIL import Image, ImageDraw, ImageFont
3
+ import requests
4
+ import io
5
+ import os
6
+ from urllib.request import urlopen
7
+
8
+ app = Flask(__name__)
9
+
10
+ def create_gradient_vertical(width, height, color_top, color_bottom):
11
+ """Create a smooth vertical gradient"""
12
+ base = Image.new('RGB', (width, height), color_top)
13
+
14
+ for y in range(height):
15
+ r = int(color_top[0] + (color_bottom[0] - color_top[0]) * y / height)
16
+ g = int(color_top[1] + (color_bottom[1] - color_top[1]) * y / height)
17
+ b = int(color_top[2] + (color_bottom[2] - color_top[2]) * y / height)
18
+
19
+ for x in range(width):
20
+ base.putpixel((x, y), (r, g, b))
21
+
22
+ return base
23
+
24
+ def get_font(size):
25
+ """Try to load Comic Sans, fallback to others"""
26
+ font_paths = [
27
+ "comic.ttf",
28
+ "C:/Windows/Fonts/comic.ttf",
29
+ "/System/Library/Fonts/Supplemental/Comic Sans MS.ttf",
30
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
31
+ "arial.ttf",
32
+ "C:/Windows/Fonts/arial.ttf"
33
+ ]
34
+
35
+ for path in font_paths:
36
+ try:
37
+ return ImageFont.truetype(path, size)
38
+ except:
39
+ continue
40
+
41
+ return ImageFont.load_default()
42
+
43
+ @app.route('/design')
44
+ def generate_card():
45
+ player_id = request.args.get('id', '89133456301399')
46
+
47
+ # Fetch player data
48
+ try:
49
+ response = requests.get(f'https://efootdb.vercel.app/api/playerinfo?id={player_id}')
50
+ data = response.json()
51
+
52
+ if not data.get('success'):
53
+ return "Player not found", 404
54
+
55
+ info = data['info']
56
+ except Exception as e:
57
+ return f"Error fetching data: {str(e)}", 500
58
+
59
+ # Card dimensions (16:9 ratio)
60
+ W, H = 1920, 1080
61
+
62
+ # Dark blue theme colors
63
+ DARK_NAVY = (10, 15, 35)
64
+ DEEP_BLUE = (15, 25, 55)
65
+ MEDIUM_BLUE = (25, 45, 85)
66
+ ACCENT_BLUE = (60, 100, 180)
67
+ LIGHT_BLUE = (100, 150, 220)
68
+ ELECTRIC_BLUE = (80, 180, 255)
69
+ WHITE = (255, 255, 255)
70
+ LIGHT_GRAY = (200, 210, 230)
71
+ GOLD = (255, 215, 0)
72
+ GREEN = (80, 220, 120)
73
+ RED = (255, 80, 80)
74
+ ORANGE = (255, 160, 60)
75
+
76
+ # Create base with gradient
77
+ img = create_gradient_vertical(W, H, DARK_NAVY, DEEP_BLUE)
78
+
79
+ # Try to load background if exists
80
+ try:
81
+ if os.path.exists('background.png'):
82
+ bg = Image.open('background.png').convert('RGB')
83
+ bg = bg.resize((W, H))
84
+ # Blend background with gradient (50% opacity)
85
+ img = Image.blend(img, bg, 0.3)
86
+ except:
87
+ pass
88
+
89
+ draw = ImageDraw.Draw(img)
90
+
91
+ # Load fonts
92
+ font_title = get_font(70)
93
+ font_name = get_font(90)
94
+ font_header = get_font(50)
95
+ font_large = get_font(42)
96
+ font_medium = get_font(36)
97
+ font_small = get_font(28)
98
+ font_tiny = get_font(24)
99
+
100
+ # === LEFT PANEL: Player Image & Basic Info ===
101
+ left_panel_x = 60
102
+ left_panel_y = 80
103
+ left_panel_w = 520
104
+ left_panel_h = 920
105
+
106
+ # Panel background
107
+ draw.rounded_rectangle(
108
+ [left_panel_x, left_panel_y, left_panel_x + left_panel_w, left_panel_y + left_panel_h],
109
+ radius=20,
110
+ fill=MEDIUM_BLUE
111
+ )
112
+
113
+ # Player image container
114
+ img_container_y = left_panel_y + 30
115
+ img_container_h = 480
116
+
117
+ draw.rounded_rectangle(
118
+ [left_panel_x + 30, img_container_y, left_panel_x + left_panel_w - 30, img_container_y + img_container_h],
119
+ radius=15,
120
+ fill=DEEP_BLUE
121
+ )
122
+
123
+ # Load and display player image
124
+ try:
125
+ player_img_response = urlopen(info['imageFront'])
126
+ player_img = Image.open(player_img_response).convert('RGBA')
127
+
128
+ # Calculate aspect ratio fit
129
+ max_w = left_panel_w - 80
130
+ max_h = img_container_h - 40
131
+
132
+ ratio = min(max_w / player_img.width, max_h / player_img.height)
133
+ new_w = int(player_img.width * ratio)
134
+ new_h = int(player_img.height * ratio)
135
+
136
+ player_img = player_img.resize((new_w, new_h), Image.Resampling.LANCZOS)
137
+
138
+ # Center the image
139
+ paste_x = left_panel_x + (left_panel_w - new_w) // 2
140
+ paste_y = img_container_y + (img_container_h - new_h) // 2
141
+
142
+ # Convert img to RGBA for pasting
143
+ img_rgba = img.convert('RGBA')
144
+ img_rgba.paste(player_img, (paste_x, paste_y), player_img)
145
+ img = img_rgba.convert('RGB')
146
+ draw = ImageDraw.Draw(img)
147
+ except:
148
+ pass
149
+
150
+ # Rating badge (circular)
151
+ badge_y = img_container_y + img_container_h + 30
152
+ badge_center_x = left_panel_x + left_panel_w // 2
153
+ badge_radius = 70
154
+
155
+ draw.ellipse(
156
+ [badge_center_x - badge_radius, badge_y - badge_radius,
157
+ badge_center_x + badge_radius, badge_y + badge_radius],
158
+ fill=RED,
159
+ outline=GOLD,
160
+ width=6
161
+ )
162
+
163
+ # Rating text
164
+ rating = info['attributes'][0]['value']
165
+ bbox = draw.textbbox((0, 0), rating, font=font_name)
166
+ text_w = bbox[2] - bbox[0]
167
+ text_h = bbox[3] - bbox[1]
168
+ draw.text(
169
+ (badge_center_x - text_w // 2, badge_y - text_h // 2 - 5),
170
+ rating,
171
+ font=font_name,
172
+ fill=WHITE
173
+ )
174
+
175
+ # Position and rarity
176
+ info_y = badge_y + badge_radius + 40
177
+
178
+ position_text = f"{info['position']} • {info['rarity']}"
179
+ bbox = draw.textbbox((0, 0), position_text, font=font_large)
180
+ text_w = bbox[2] - bbox[0]
181
+ draw.text(
182
+ (badge_center_x - text_w // 2, info_y),
183
+ position_text,
184
+ font=font_large,
185
+ fill=GOLD
186
+ )
187
+
188
+ # Playing style
189
+ style_y = info_y + 50
190
+ style_text = info['playingStyle']
191
+ bbox = draw.textbbox((0, 0), style_text, font=font_medium)
192
+ text_w = bbox[2] - bbox[0]
193
+ draw.text(
194
+ (badge_center_x - text_w // 2, style_y),
195
+ style_text,
196
+ font=font_medium,
197
+ fill=LIGHT_BLUE
198
+ )
199
+
200
+ # Physical stats
201
+ phys_y = style_y + 70
202
+ phys_stats = [
203
+ f"Height: {info['height']}cm",
204
+ f"Weight: {info['weight']}kg",
205
+ f"Age: {info['age']}",
206
+ f"Foot: {info['foot']}"
207
+ ]
208
+
209
+ for stat in phys_stats:
210
+ bbox = draw.textbbox((0, 0), stat, font=font_small)
211
+ text_w = bbox[2] - bbox[0]
212
+ draw.text(
213
+ (badge_center_x - text_w // 2, phys_y),
214
+ stat,
215
+ font=font_small,
216
+ fill=LIGHT_GRAY
217
+ )
218
+ phys_y += 38
219
+
220
+ # === TOP RIGHT: Header Section ===
221
+ header_x = left_panel_x + left_panel_w + 60
222
+ header_y = 80
223
+ header_w = W - header_x - 60
224
+ header_h = 180
225
+
226
+ draw.rounded_rectangle(
227
+ [header_x, header_y, header_x + header_w, header_y + header_h],
228
+ radius=20,
229
+ fill=MEDIUM_BLUE
230
+ )
231
+
232
+ # Title
233
+ draw.text((header_x + 40, header_y + 25), "PLAYER PROFILE", font=font_title, fill=ELECTRIC_BLUE)
234
+
235
+ # Player name
236
+ draw.text((header_x + 40, header_y + 100), info['playername'], font=font_name, fill=WHITE)
237
+
238
+ # === MIDDLE RIGHT: Team & Info ===
239
+ team_y = header_y + header_h + 30
240
+ team_h = 160
241
+
242
+ draw.rounded_rectangle(
243
+ [header_x, team_y, header_x + header_w, team_y + team_h],
244
+ radius=20,
245
+ fill=MEDIUM_BLUE
246
+ )
247
+
248
+ # Team info
249
+ team_info_y = team_y + 30
250
+ team_items = [
251
+ ("Team:", info['teamname']),
252
+ ("League:", info['league']),
253
+ ("Nation:", f"{info['nationality']} ({info['region']})")
254
+ ]
255
+
256
+ for label, value in team_items:
257
+ draw.text((header_x + 40, team_info_y), label, font=font_medium, fill=LIGHT_BLUE)
258
+ draw.text((header_x + 230, team_info_y), value, font=font_medium, fill=WHITE)
259
+ team_info_y += 42
260
+
261
+ # Squad number badge
262
+ squad_badge_x = header_x + header_w - 120
263
+ squad_badge_y = team_y + 30
264
+ squad_size = 100
265
+
266
+ draw.rounded_rectangle(
267
+ [squad_badge_x, squad_badge_y, squad_badge_x + squad_size, squad_badge_y + squad_size],
268
+ radius=15,
269
+ fill=DEEP_BLUE,
270
+ outline=GOLD,
271
+ width=4
272
+ )
273
+
274
+ squad_num = info['squadnumber']
275
+ bbox = draw.textbbox((0, 0), squad_num, font=font_name)
276
+ text_w = bbox[2] - bbox[0]
277
+ text_h = bbox[3] - bbox[1]
278
+ draw.text(
279
+ (squad_badge_x + (squad_size - text_w) // 2, squad_badge_y + (squad_size - text_h) // 2 - 5),
280
+ squad_num,
281
+ font=font_name,
282
+ fill=GOLD
283
+ )
284
+
285
+ # === BOTTOM RIGHT: Stats Section ===
286
+ stats_y = team_y + team_h + 30
287
+ stats_h = H - stats_y - 80
288
+
289
+ draw.rounded_rectangle(
290
+ [header_x, stats_y, header_x + header_w, stats_y + stats_h],
291
+ radius=20,
292
+ fill=MEDIUM_BLUE
293
+ )
294
+
295
+ # Stats header
296
+ draw.text((header_x + 40, stats_y + 25), "KEY ATTRIBUTES", font=font_header, fill=ELECTRIC_BLUE)
297
+
298
+ # Key stats to display (top performance stats)
299
+ key_stats = [
300
+ ('Offensive Awareness', info['attributes'][1]['value'], info['attributes'][1].get('booster')),
301
+ ('Dribbling', info['attributes'][3]['value'], info['attributes'][3].get('booster')),
302
+ ('Tight Possession', info['attributes'][4]['value'], info['attributes'][4].get('booster')),
303
+ ('Ball Control', info['attributes'][2]['value'], info['attributes'][2].get('booster')),
304
+ ('Acceleration', info['attributes'][21]['value'], info['attributes'][21].get('booster')),
305
+ ('Speed', info['attributes'][20]['value'], info['attributes'][20].get('booster')),
306
+ ('Finishing', info['attributes'][7]['value'], info['attributes'][7].get('booster')),
307
+ ('Balance', info['attributes'][25]['value'], info['attributes'][25].get('booster')),
308
+ ]
309
+
310
+ stat_y = stats_y + 90
311
+
312
+ for stat_name, value, booster in key_stats:
313
+ # Stat name
314
+ draw.text((header_x + 40, stat_y), stat_name, font=font_medium, fill=LIGHT_GRAY)
315
+
316
+ # Progress bar background
317
+ bar_x = header_x + 380
318
+ bar_w = 500
319
+ bar_h = 28
320
+
321
+ draw.rounded_rectangle(
322
+ [bar_x, stat_y + 5, bar_x + bar_w, stat_y + 5 + bar_h],
323
+ radius=14,
324
+ fill=DEEP_BLUE
325
+ )
326
+
327
+ # Progress bar fill
328
+ try:
329
+ val_int = int(value)
330
+ filled_w = int((val_int / 99) * bar_w)
331
+
332
+ # Color based on value
333
+ if val_int >= 90:
334
+ bar_color = GREEN
335
+ elif val_int >= 80:
336
+ bar_color = LIGHT_BLUE
337
+ elif val_int >= 70:
338
+ bar_color = ORANGE
339
+ else:
340
+ bar_color = ACCENT_BLUE
341
+
342
+ if filled_w > 0:
343
+ draw.rounded_rectangle(
344
+ [bar_x, stat_y + 5, bar_x + filled_w, stat_y + 5 + bar_h],
345
+ radius=14,
346
+ fill=bar_color
347
+ )
348
+ except:
349
+ pass
350
+
351
+ # Value text
352
+ value_text = value
353
+ if booster:
354
+ value_text = f"{value} {booster}"
355
+
356
+ draw.text((bar_x + bar_w + 25, stat_y + 2), value_text, font=font_large, fill=WHITE)
357
+
358
+ stat_y += 55
359
+
360
+ # Skills section
361
+ skills_x = header_x + 40
362
+ skills_y = stat_y + 20
363
+
364
+ draw.text((skills_x, skills_y), "Special Skills:", font=font_medium, fill=LIGHT_BLUE)
365
+ skills_y += 40
366
+
367
+ skills = info.get('playerSkills', [])
368
+ skill_text = " • ".join(skills[:6]) # Top 6 skills
369
+
370
+ # Word wrap for skills
371
+ words = skill_text.split()
372
+ lines = []
373
+ current_line = []
374
+
375
+ for word in words:
376
+ test_line = ' '.join(current_line + [word])
377
+ bbox = draw.textbbox((0, 0), test_line, font=font_small)
378
+ if bbox[2] - bbox[0] <= header_w - 100:
379
+ current_line.append(word)
380
+ else:
381
+ if current_line:
382
+ lines.append(' '.join(current_line))
383
+ current_line = [word]
384
+
385
+ if current_line:
386
+ lines.append(' '.join(current_line))
387
+
388
+ for line in lines[:2]: # Max 2 lines
389
+ draw.text((skills_x, skills_y), line, font=font_small, fill=LIGHT_GRAY)
390
+ skills_y += 32
391
+
392
+ # === FOOTER ===
393
+ footer_y = H - 50
394
+ draw.text((60, footer_y), f"ID: {player_id}", font=font_tiny, fill=LIGHT_GRAY)
395
+ draw.text((W - 350, footer_y), f"Max Level: {info['maximumlevel']} • Form: {info['attributes'][28]['value']}", font=font_tiny, fill=LIGHT_GRAY)
396
+
397
+ # Save to bytes
398
+ img_io = io.BytesIO()
399
+ img.save(img_io, 'PNG', quality=95)
400
+ img_io.seek(0)
401
+
402
+ return send_file(img_io, mimetype='image/png')
403
+
404
+ if __name__ == '__main__':
405
+ app.run(debug=True, host='0.0.0.0', port=5000)