Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 140 additions & 142 deletions thumbnail.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,135 +11,193 @@
API_TOKEN = os.getenv("BOT_TOKEN", "7597391690:AAFdUlJBP46IJNvkaM6vIhW6J1fbmUTlkjA")
bot = telebot.TeleBot(API_TOKEN)

# Fonts
FONTS_DIR = "fonts"
try:
# Use root BebasNeue-Regular.ttf for title (the one in fonts/ is empty)
TITLE_FONT = ImageFont.truetype("BebasNeue-Regular.ttf", 160)
# Use available Roboto variants (Bold, Medium, Regular are empty in fonts/)
BOLD_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-SemiBold.ttf"), 42)
MEDIUM_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-SemiBold.ttf"), 36)
REG_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Light.ttf"), 30)
GENRE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-SemiBold.ttf"), 38)
except:
TITLE_FONT = ImageFont.load_default()
BOLD_FONT = ImageFont.load_default()
MEDIUM_FONT = ImageFont.load_default()
REG_FONT = ImageFont.load_default()
GENRE_FONT = ImageFont.load_default()

LOGO_FONT = MEDIUM_FONT

CANVAS_WIDTH, CANVAS_HEIGHT = 1280, 720

# Colors
BG_COLOR = (20, 35, 60) # Bluish Dark Background
HEX_OUTLINE = (40, 55, 85) # Lighter blue outline for background grid
TEXT_COLOR = (255, 255, 255)
SUBTEXT_COLOR = (210, 210, 210)
GENRE_COLOR = (140, 150, 190) # Bluish grey
BUTTON_BG = (40, 60, 100) # Blue button bg
LOGO_COLOR = (255, 255, 255)
HONEYCOMB_OUTLINE_COLOR = (255, 255, 255)
HONEYCOMB_STROKE = 6

# Helper Functions
def draw_regular_polygon(draw, center, radius, n_sides=6, rotation=30, fill=None, outline=None, width=1):
points = []
for i in range(n_sides):
angle = math.radians(rotation + 360 / n_sides * i)
x = center[0] + radius * math.cos(angle)
y = center[1] + radius * math.sin(angle)
points.append((x, y))
if fill:
draw.polygon(points, fill=fill)
if outline:
draw.polygon(points, outline=outline, width=width)

def generate_hex_background():
# Use the hex_bg.png from fonts folder if available
hex_bg_path = os.path.join(FONTS_DIR, "hex_bg.png")
if os.path.exists(hex_bg_path):
try:
return Image.open(hex_bg_path).convert("RGBA")
except Exception:
pass # Fall through to programmatic generation

# Otherwise generate programmatically
img = Image.new("RGBA", (CANVAS_WIDTH, CANVAS_HEIGHT), BG_COLOR)
draw = ImageDraw.Draw(img)

# Background Grid
hex_radius = 55
import math
dx = math.sqrt(3) * hex_radius
dy = 1.5 * hex_radius

cols = int(CANVAS_WIDTH / dx) + 2
rows = int(CANVAS_HEIGHT / dy) + 2

for row in range(rows):
for col in range(cols):
cx = col * dx
cy = row * dy
if row % 2 == 1:
cx += dx / 2
def get_font(font_name, size):
"""Try to load a font from multiple potential locations."""
possible_paths = [
font_name,
os.path.join("fonts", font_name),
os.path.join("/app/fonts", font_name),
os.path.join(os.getcwd(), "fonts", font_name)
]

for path in possible_paths:
if os.path.exists(path):
try:
return ImageFont.truetype(path, size)
except OSError:
continue

print(f"Warning: Could not load font {font_name}, using default.")
return ImageFont.load_default()

# Load Fonts
TITLE_FONT = get_font("BebasNeue-Regular.ttf", 160)
BOLD_FONT = get_font("Roboto-Bold.ttf", 42)
MEDIUM_FONT = get_font("Roboto-Medium.ttf", 36)
REG_FONT = get_font("Roboto-Light.ttf", 30)
GENRE_FONT = get_font("Roboto-Medium.ttf", 38)
LOGO_FONT = MEDIUM_FONT

# Draw faint outline
draw_regular_polygon(draw, (cx, cy), hex_radius, outline=HEX_OUTLINE, width=2)
def wrap_text(text, font, max_width):
try:
avg_char_width = font.getlength('x')
except AttributeError:
# Fallback for default font which might not have getlength in older Pillow
avg_char_width = 10

return img
if avg_char_width == 0: avg_char_width = 10

def wrap_text(text, font, max_width):
avg_char_width = font.getlength('x')
chars_per_line = int(max_width / avg_char_width)
wrapped = textwrap.fill(text, width=chars_per_line)
return wrapped.split('\n')

def add_gradient(image):
# Add a horizontal gradient from black (left) to transparent (right)
# to make text readable on the left side
width, height = image.size
gradient = Image.new('L', (width, height), color=0)
draw = ImageDraw.Draw(gradient)

for x in range(width):
if x < 400:
alpha = 240 # Very dark on left
elif x < 900:
# Linear fade
alpha = int(240 * (1 - (x - 400) / 500))
else:
alpha = 0

draw.line([(x, 0), (x, height)], fill=alpha)

black_layer = Image.new("RGBA", (width, height), (0, 0, 0, 0))
black_layer.putalpha(gradient)
image.paste(black_layer, (0, 0), black_layer)
return image

def generate_thumbnail(anime):
title = anime['title']['english'] or anime['title']['romaji']
poster_url = anime['coverImage']['extraLarge']
score = anime['averageScore']
genres = anime['genres'][:3]
desc = (anime['description'] or "").replace("<br>", " ").replace("<i>", "").replace("</i>", "")
desc = " ".join(desc.split()[:40]) + "..." # Shorter desc for larger text
desc = " ".join(desc.split()[:40]) + "..."

# 1. Prepare Background
if os.path.exists("background.jpg"):
bg = Image.open("background.jpg").convert("RGBA")
bg = bg.resize((CANVAS_WIDTH, CANVAS_HEIGHT), Image.Resampling.LANCZOS)
else:
bg = Image.new("RGBA", (CANVAS_WIDTH, CANVAS_HEIGHT), (20, 35, 60))

# Background
bg = generate_hex_background()
# Apply gradient to background for text visibility
bg = add_gradient(bg)
canvas = bg.copy()
draw = ImageDraw.Draw(canvas)

# 1. Logo (Top Left)
# 2. Prepare Poster & Mask
try:
poster_resp = requests.get(poster_url)
poster = Image.open(BytesIO(poster_resp.content)).convert("RGBA")

# Load PLP mask
if os.path.exists("plp.jpg"):
mask_img = Image.open("plp.jpg").convert("L")

# Resize mask logic
# User wants it "bilkul right side"
# We resize mask to have height=720, preserving aspect ratio (if it's not 720)
# Then paste it at the right edge

m_width, m_height = mask_img.size
target_h = CANVAS_HEIGHT

if m_height != target_h:
aspect = m_width / m_height
new_w = int(target_h * aspect)
mask_img = mask_img.resize((new_w, target_h), Image.Resampling.LANCZOS)

m_width, m_height = mask_img.size

# Calculate position: Align to Right
paste_x = CANVAS_WIDTH - m_width
paste_y = 0 # Top aligned

# Now prepare poster to be same size as resized mask
# Resize poster to cover the mask area (cover)
p_aspect = poster.width / poster.height
m_aspect = m_width / m_height

if p_aspect > m_aspect:
# Poster is wider, fit to height
new_h = m_height
new_w = int(new_h * p_aspect)
poster = poster.resize((new_w, new_h), Image.Resampling.LANCZOS)
# Center crop horizontally
left = (new_w - m_width) // 2
poster = poster.crop((left, 0, left + m_width, new_h))
else:
# Poster is taller, fit to width
new_w = m_width
new_h = int(new_w / p_aspect)
poster = poster.resize((new_w, new_h), Image.Resampling.LANCZOS)
# Crop top/center
poster = poster.crop((0, 0, m_width, m_height))

# Composite
# Paste poster onto canvas at paste_x, paste_y using mask
canvas.paste(poster, (paste_x, paste_y), mask_img)

else:
print("Warning: plp.jpg not found")

except Exception as e:
print(f"Error loading poster/mask: {e}")
import traceback
traceback.print_exc()

# 3. Draw Text & UI (Left Side)

# Logo
icon_x, icon_y = 50, 40
sz = 18
draw.polygon([(icon_x, icon_y+sz), (icon_x+sz, icon_y), (icon_x+2*sz, icon_y+sz), (icon_x+sz, icon_y+2*sz)], outline=LOGO_COLOR, width=3)
draw.polygon([(icon_x+10, icon_y+sz), (icon_x+sz+10, icon_y), (icon_x+2*sz+10, icon_y+sz), (icon_x+sz+10, icon_y+2*sz)], outline=LOGO_COLOR, width=3)
draw.text((icon_x + 65, icon_y + 2), "ANIME FLICKER", font=LOGO_FONT, fill=LOGO_COLOR)

# 2. Rating (Below Logo)
# Rating
if score:
rating_text = f"{score/10:.1f}+ Rating"
draw.text((50, 140), rating_text, font=REG_FONT, fill=TEXT_COLOR)

# 3. Title (Large, Below Rating)
# Title
title_lines = wrap_text(title.upper(), TITLE_FONT, 700)
title_y = 180
for line in title_lines[:2]:
draw.text((50, title_y), line, font=TITLE_FONT, fill=TEXT_COLOR)
title_y += 140 # Height
title_y += 140

# 4. Genres (Below Title)
genre_text = ", ".join(genres).upper()
draw.text((50, title_y + 10), genre_text, font=GENRE_FONT, fill=GENRE_COLOR)
# Genres
if genres:
genre_text = ", ".join(genres).upper()
draw.text((50, title_y + 10), genre_text, font=GENRE_FONT, fill=GENRE_COLOR)

# 5. Description (Below Genres)
# Description
desc_y = title_y + 60
desc_lines = wrap_text(desc, REG_FONT, 580)
for line in desc_lines[:4]: # Fewer lines because text is bigger
for line in desc_lines[:4]:
draw.text((50, desc_y), line, font=REG_FONT, fill=SUBTEXT_COLOR)
desc_y += 42

# 6. Buttons (Bottom Left)
# Buttons
btn_y = 620
btn_width = 210
btn_height = 65
Expand All @@ -157,66 +215,6 @@ def generate_thumbnail(anime):
text_x = btn2_x + (btn_width - text_w) / 2
draw.text((text_x, btn_y + 12), "JOIN NOW", font=BOLD_FONT, fill=TEXT_COLOR)


# 7. Right Side Honeycomb Poster
poster_resp = requests.get(poster_url)
poster = Image.open(BytesIO(poster_resp.content)).convert("RGBA")

start_x = 550
width = CANVAS_WIDTH - start_x + 100
height = CANVAS_HEIGHT

# Resize poster
aspect = poster.width / poster.height
target_aspect = width / height

if aspect > target_aspect:
new_height = height
new_width = int(new_height * aspect)
poster = poster.resize((new_width, new_height), Image.Resampling.LANCZOS)
left = (new_width - width) // 2
poster = poster.crop((left, 0, left + width, new_height))
else:
new_width = width
new_height = int(new_width / aspect)
poster = poster.resize((new_width, new_height), Image.Resampling.LANCZOS)
top = (new_height - height) // 2
poster = poster.crop((0, top, width, top + height))

# Mask Generation
mask = Image.new("L", (width, height), 0)
mask_draw = ImageDraw.Draw(mask)

overlay = Image.new("RGBA", (width, height), (0,0,0,0))
overlay_draw = ImageDraw.Draw(overlay)

hex_radius = 160
gap = 8

dx = math.sqrt(3) * hex_radius
dy = 1.5 * hex_radius

cols = int(CANVAS_WIDTH / dx) + 2
rows = int(CANVAS_HEIGHT / dy) + 2

for row in range(-1, rows):
for col in range(-1, cols):
global_cx = col * dx
if row % 2 == 1:
global_cx += dx / 2
global_cy = row * dy

local_cx = global_cx - start_x
local_cy = global_cy

if global_cx > 650:
draw_regular_polygon(mask_draw, (local_cx, local_cy), hex_radius - gap, fill=255)
draw_regular_polygon(overlay_draw, (local_cx, local_cy), hex_radius - gap, outline=HONEYCOMB_OUTLINE_COLOR, width=HONEYCOMB_STROKE)

poster.putalpha(mask)
canvas.paste(poster, (start_x, 0), poster)
canvas.paste(overlay, (start_x, 0), overlay)

final = BytesIO()
canvas.convert("RGB").save(final, "PNG")
final.seek(0)
Expand Down