Spaces:
Runtime error
Runtime error
| # utils/generators/profile.py | |
| import io | |
| from PIL import Image, ImageOps, ImageFilter, ImageDraw | |
| from .base import BaseGenerator | |
| from .legendary import PremiumGenerator | |
| from .common import CommonGenerator | |
| from .custom import CustomGenerator | |
| class ProfileGenerator(BaseGenerator): | |
| def build_profile(self, username: str, stats: dict, showcase_bytes: bytes = None, team_data: list = None, avatar_bytes: bytes = None) -> io.BytesIO: | |
| card_w, card_h = 580, 820 | |
| gap = 10 | |
| margin = 15 | |
| columns, rows = 4, 2 | |
| padding = 40 | |
| W = (margin * 2) + (card_w * columns) + (gap * (columns - 1)) | |
| H = (margin * 2) + (card_h * rows) + (gap * (rows - 1)) | |
| canvas = Image.new("RGBA", (W, H), (0, 0, 0, 255)) | |
| cleanup = [canvas] | |
| try: | |
| shadow = Image.new("RGBA", (card_w + 20, card_h + 20), (0, 0, 0, 0)) | |
| shadow_draw = ImageDraw.Draw(shadow) | |
| shadow_draw.rectangle([4, 4, card_w + 16, card_h + 16], fill=(0, 0, 0, 180)) | |
| pre_rendered_shadow = shadow.filter(ImageFilter.GaussianBlur(12)) | |
| cleanup.append(pre_rendered_shadow) | |
| if team_data: | |
| # ⚡ THE FIX: Filter out empty slots so cards automatically shift left and fill gaps! | |
| active_cards = [card for card in team_data if card and card.get('bytes')] | |
| for i, t_char in enumerate(active_cards): | |
| if i >= 8: break # Max 8 cards on the grid | |
| col = i % columns | |
| row = i // columns | |
| inner_x = margin + col * (card_w + gap) | |
| inner_y = margin + row * (card_h + gap) | |
| try: | |
| if t_char.get('custom_frame_url') and t_char.get('frame_bytes'): | |
| generator = CustomGenerator() | |
| full_card = generator.build_template(t_char['bytes'], t_char['frame_bytes'], t_char['name']) | |
| elif t_char.get('rarity') in ["SSR", "UR"]: | |
| generator = PremiumGenerator() | |
| full_card = generator.build_template(t_char['bytes'], t_char.get('element', 'None')) | |
| else: | |
| generator = CommonGenerator() | |
| full_card = generator.build_template(t_char['bytes'], t_char.get('element', 'None')) | |
| cleanup.append(full_card) | |
| text_io = generator.apply_text( | |
| full_card, | |
| t_char.get('name', 'Unknown'), | |
| t_char.get('print_num', 1), | |
| t_char.get('element', 'None'), | |
| t_char.get('rarity', 'C') | |
| ) | |
| cleanup.append(text_io) | |
| finished_card = Image.open(text_io).convert("RGBA") | |
| cleanup.append(finished_card) | |
| if finished_card.size == (660, 900): | |
| cropped_card = finished_card.crop(( | |
| padding, | |
| padding, | |
| finished_card.width - padding, | |
| finished_card.height - padding | |
| )) | |
| else: | |
| cropped_card = finished_card | |
| cleanup.append(cropped_card) | |
| if cropped_card.size != (card_w, card_h): | |
| cropped_card = cropped_card.resize((card_w, card_h), Image.Resampling.LANCZOS) | |
| cleanup.append(cropped_card) | |
| canvas.paste(pre_rendered_shadow, (inner_x - 6, inner_y - 6), pre_rendered_shadow) | |
| canvas.paste(cropped_card, (inner_x, inner_y), cropped_card) | |
| except Exception as e: | |
| print(f"Failed to build card {i}: {e}") | |
| buf = io.BytesIO() | |
| canvas.save(buf, format="WEBP", quality=95) | |
| buf.seek(0) | |
| return buf | |
| finally: | |
| for img in cleanup: | |
| try: | |
| img.close() | |
| except: | |
| pass | |