diff --git a/Procfile b/Procfile
new file mode 100644
index 0000000..8c154e4
--- /dev/null
+++ b/Procfile
@@ -0,0 +1 @@
+worker: python thumbnail.py
diff --git a/background.jpg b/background.jpg
new file mode 100644
index 0000000..562f12d
Binary files /dev/null and b/background.jpg differ
diff --git a/fonts/BebasNeue-Regular.ttf b/fonts/BebasNeue-Regular.ttf
index d2190b5..e69de29 100644
Binary files a/fonts/BebasNeue-Regular.ttf and b/fonts/BebasNeue-Regular.ttf differ
diff --git a/fonts/Roboto-Bold.ttf b/fonts/Roboto-Bold.ttf
index 4658f9a..e69de29 100644
Binary files a/fonts/Roboto-Bold.ttf and b/fonts/Roboto-Bold.ttf differ
diff --git a/fonts/Roboto-Medium.ttf b/fonts/Roboto-Medium.ttf
index d629e98..e69de29 100644
Binary files a/fonts/Roboto-Medium.ttf and b/fonts/Roboto-Medium.ttf differ
diff --git a/fonts/Roboto-Regular.ttf b/fonts/Roboto-Regular.ttf
index 7e3bb2f..e69de29 100644
Binary files a/fonts/Roboto-Regular.ttf and b/fonts/Roboto-Regular.ttf differ
diff --git a/plp.jpg b/plp.jpg
new file mode 100644
index 0000000..13cf379
Binary files /dev/null and b/plp.jpg differ
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..19dd462
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+pyTelegramBotAPI
+requests
+Pillow
diff --git a/thumbnail.py b/thumbnail.py
index 8a113db..7631f3b 100644
--- a/thumbnail.py
+++ b/thumbnail.py
@@ -1,197 +1,294 @@
-# thumbnail_bot.py - Final AnimeFlicker Exact Match Generator
-import telebot
-import requests
-import os
-import math
-from PIL import Image, ImageDraw, ImageFont
-import textwrap
-from io import BytesIO
-
-# ⚠️ YAHAN APNA NAYA TOKEN DAALNA (purana wala mat daalna)
-API_TOKEN = "7597391690:AAFdUlJBP46IJNvkaM6vIhW6J1fbmUTlkjA" # ← ABHI KE LIYE RAKH RAHA, PAR NAYA BANA KE CHANGE KAR DENA
-bot = telebot.TeleBot(API_TOKEN)
-
-# Fonts (fonts folder bana ke daal do)
-FONTS_DIR = "fonts"
-try:
- TITLE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "BebasNeue-Regular.ttf"), 90)
- BOLD_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Bold.ttf"), 50)
- MEDIUM_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Medium.ttf"), 40)
- REG_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Regular.ttf"), 34)
- GENRE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "coolvetica rg.otf"), 40)
-except:
- TITLE_FONT = BOLD_FONT = MEDIUM_FONT = REG_FONT = GENRE_FONT = ImageFont.load_default()
-
-BG_PATH = "hex_bg.png"
-CANVAS_WIDTH, CANVAS_HEIGHT = 1280, 720
-
-# Colors exact from example
-BG_COLOR = (20, 25, 40)
-HEX_OUTLINE = (35, 40, 60) # Darker outline
-DEC_HEX_FILL = (112, 124, 140)
-TEXT_COLOR = (255, 255, 255)
-SUBTEXT_COLOR = (220, 220, 220)
-GENRE_COLOR = (170, 170, 255)
-RATING_BG = (0, 100, 255)
-BUTTON_LEFT = (30, 40, 70)
-BUTTON_RIGHT = (0, 110, 255)
-LOGO_COLOR = (255, 255, 255)
-
-# 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 create_hexagon_mask(size):
- mask = Image.new("L", size, 0)
- draw = ImageDraw.Draw(mask)
- center = (size[0] // 2, size[1] // 2)
- radius = min(size) // 2 - 10
- draw_regular_polygon(draw, center, radius, fill=255)
- return mask
-
-def generate_hex_background():
- if os.path.exists(BG_PATH):
- return Image.open(BG_PATH).convert("RGBA")
-
- img = Image.new("RGBA", (CANVAS_WIDTH, CANVAS_HEIGHT), BG_COLOR)
- draw = ImageDraw.Draw(img)
-
- # Exact hex grid from example
- for x in range(0, 1300, 120):
- for y in range(0, 800, 104):
- center = (x + 60, y + 52)
- draw_regular_polygon(draw, center, 58, outline=HEX_OUTLINE, width=2)
-
- # Exact decorative hex positions and scales from XML (adjusted for 1280x720)
- dec_positions = [
- # From XML: locations and scales
- {"loc": (1509.906494, 1101.221924), "scale": 2.195},
- {"loc": (1315.748169, 761.138306), "scale": 2.195},
- {"loc": (1511.625122, 421.910461), "scale": 2.195},
- {"loc": (1707.117798, 761.138306), "scale": 2.195},
- {"loc": (1118.221436, 421.910461), "scale": 2.195},
- {"loc": (1315.748169, 167.000000), "scale": 1.360},
- {"loc": (1801.112549, 480.854706), "scale": 1.045},
- {"loc": (1014.503906, 710.656128), "scale": 1.141},
- {"loc": (1606.591919, 141.915283), "scale": 1.040}
- ]
- base_radius = 100
- for pos in dec_positions:
- cx, cy = pos["loc"]
- scale = pos["scale"]
- adjusted_cx = (cx / 1920) * CANVAS_WIDTH
- adjusted_cy = (cy / 1080) * CANVAS_HEIGHT
- radius = base_radius * scale * (CANVAS_HEIGHT / 1080)
- if 0 < adjusted_cx < CANVAS_WIDTH and 0 < adjusted_cy < CANVAS_HEIGHT:
- draw_regular_polygon(draw, (adjusted_cx, adjusted_cy), radius * 0.5, fill=DEC_HEX_FILL) # Scaled down
-
- img.save(BG_PATH)
- return img
-
-def wrap_text(text, font, max_width):
- lines = textwrap.wrap(text, width=max_width // (font.getlength(' ') * 1.5)) # Approximate char width
- return lines
-
-def generate_thumbnail(anime):
- title = anime['title']['english'] or anime['title']['romaji']
- poster_url = anime['coverImage']['extraLarge']
- score = anime['averageScore']
- genres = anime['genres'][:4]
- desc = (anime['description'] or "").replace("
", " ").replace("", "").replace("", "")
- desc = " ".join(desc.split()[:65]) + "..."
-
- # Background
- bg = generate_hex_background()
- canvas = bg.copy()
- draw = ImageDraw.Draw(canvas)
-
- # Logo exact
- draw.text((80, 10), "ANIME FLICKER", font=MEDIUM_FONT, fill=LOGO_COLOR)
-
- # Poster hex exact
- poster_resp = requests.get(poster_url)
- poster = Image.open(BytesIO(poster_resp.content)).convert("RGBA")
- poster = poster.resize((440, 620))
- mask = create_hexagon_mask((440, 620))
- hex_poster = Image.new("RGBA", (440, 620), (0,0,0,0))
- hex_poster.paste(poster, (0,0), mask)
- canvas.paste(hex_poster, (780, 50), hex_poster)
-
- # Title exact position and upper
- draw.text((80, 70), title.upper(), font=TITLE_FONT, fill=TEXT_COLOR)
-
- # Rating exact
- if score:
- rating = f"{score/10:.1f}+ Rating"
- draw.rounded_rectangle((80, 190, 340, 250), radius=20, fill=RATING_BG)
- draw.text((100, 200), rating, font=MEDIUM_FONT, fill=TEXT_COLOR)
-
- # Genres exact: " • " join, upper
- genre_text = " • ".join(genres).upper()
- draw.text((80, 290), genre_text, font=REG_FONT, fill=GENRE_COLOR)
-
- # Description exact wrap and position
- lines = wrap_text(desc, REG_FONT, 52)
- y = 360
- for line in lines:
- draw.text((80, y), line, font=REG_FONT, fill=SUBTEXT_COLOR)
- y += 44
-
- # Buttons exact
- draw.rounded_rectangle((80, 580, 320, 660), radius=15, fill=BUTTON_LEFT)
- draw.text((110, 600), "DOWNLOAD", font=BOLD_FONT, fill=TEXT_COLOR)
- draw.rounded_rectangle((350, 580, 590, 660), radius=15, fill=BUTTON_RIGHT)
- draw.text((400, 600), "JOIN NOW", font=BOLD_FONT, fill=TEXT_COLOR)
-
- # Final
- final = BytesIO()
- canvas.convert("RGB").save(final, "PNG")
- final.seek(0)
- return final
-
-@bot.message_handler(commands=['start'])
-def start(msg):
- bot.reply_to(msg, "Anime ka naam bhej ya /thumb Haikyuu likh\nMain ekdam AnimeFlicker jaisa thumbnail bana dunga")
-
-@bot.message_handler(commands=['thumb'])
-def thumb(msg):
- query = msg.text.replace("/thumb", "").strip()
- if not query:
- bot.reply_to(msg, "Bhai anime naam toh likh na!\nExample: /thumb One Piece")
- return
- bot.send_chat_action(msg.chat.id, 'upload_photo')
- bot.reply_to(msg, "Thumbnail ban raha hai... 10 sec ruk")
- try:
- resp = requests.post("https://graphql.anilist.co",
- json={"query": """
- query ($search: String) {
- Media(search: $search, type: ANIME) {
- title { romaji english }
- coverImage { extraLarge }
- averageScore
- genres
- description
- }
- }
- """, "variables": {"search": query}}
- ).json()['data']['Media']
- if not resp:
- bot.reply_to(msg, "Anime nahi mila bhai, sahi spelling likh")
- return
- img = generate_thumbnail(resp)
- bot.send_photo(msg.chat.id, img,
- caption=f"{resp['title']['english'] or resp['title']['romaji']}\n@YourChannelHere")
- except Exception as e:
- bot.reply_to(msg, f"Error: {e}\nBot restart kar ya font daal")
-
-print("Bot chal gaya!")
-bot.infinity_polling()
\ No newline at end of file
+# thumbnail_bot.py - Final AnimeFlicker Exact Match Generator
+import telebot
+import requests
+import os
+import math
+from PIL import Image, ImageDraw, ImageFont, ImageFilter
+import textwrap
+from io import BytesIO
+
+# ⚠️ YAHAN APNA NAYA TOKEN DAALNA (purana wala mat daalna)
+API_TOKEN = os.getenv("BOT_TOKEN", "YOUR_BOT_TOKEN_HERE") # ← Replace with your actual token
+bot = telebot.TeleBot(API_TOKEN)
+
+# Fonts
+FONTS_DIR = "fonts"
+try:
+ TITLE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "BebasNeue-Regular.ttf"), 110)
+ BOLD_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Bold.ttf"), 30)
+ MEDIUM_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Medium.ttf"), 25)
+ REG_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Regular.ttf"), 22)
+ GENRE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Condensed-Bold.ttf"), 26) # Fallback to Roboto-Bold if not found
+except:
+ # Try finding condensed
+ try:
+ GENRE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto_Condensed-Bold.ttf"), 26)
+ except:
+ try:
+ GENRE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Bold.ttf"), 26)
+ except:
+ GENRE_FONT = ImageFont.load_default()
+
+ try:
+ TITLE_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "BebasNeue-Regular.ttf"), 110)
+ except:
+ TITLE_FONT = ImageFont.load_default()
+
+ try:
+ BOLD_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Bold.ttf"), 30)
+ MEDIUM_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Medium.ttf"), 25)
+ REG_FONT = ImageFont.truetype(os.path.join(FONTS_DIR, "Roboto-Regular.ttf"), 22)
+ except:
+ BOLD_FONT = MEDIUM_FONT = REG_FONT = ImageFont.load_default()
+
+LOGO_FONT = BOLD_FONT
+
+BG_PATH = "hex_bg.png"
+CANVAS_WIDTH, CANVAS_HEIGHT = 1280, 720
+
+# Colors
+BG_COLOR = (15, 20, 35) # Dark Blue/Black
+HEX_OUTLINE = (40, 50, 70) # Faint outline for background
+TEXT_COLOR = (255, 255, 255)
+SUBTEXT_COLOR = (200, 200, 200)
+GENRE_COLOR = (100, 110, 130)
+BUTTON_BG = (25, 30, 45) # Dark button bg
+LOGO_COLOR = (255, 255, 255)
+HONEYCOMB_OUTLINE_COLOR = (200, 220, 255) # Light outline for poster honeycombs
+
+# 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():
+ img = Image.new("RGBA", (CANVAS_WIDTH, CANVAS_HEIGHT), BG_COLOR)
+ draw = ImageDraw.Draw(img)
+
+ # Background Grid (Left side mainly)
+ # Use larger hexagons
+ hex_radius = 60
+ 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
+
+ # Draw faint outline
+ draw_regular_polygon(draw, (cx, cy), hex_radius, outline=HEX_OUTLINE, width=2)
+
+ return img
+
+def wrap_text(text, font, max_width):
+ # Estimate characters per line
+ 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 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("
", " ").replace("", "").replace("", "")
+ desc = " ".join(desc.split()[:50]) + "..."
+
+ # Background
+ bg = generate_hex_background()
+ canvas = bg.copy()
+ draw = ImageDraw.Draw(canvas)
+
+ # 1. Logo (Top Left)
+ icon_x, icon_y = 50, 40
+ # Simple diamond shape icon for logo
+ # Draw two overlapping diamonds/squares
+ sz = 12
+ 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+8, icon_y+sz), (icon_x+sz+8, icon_y), (icon_x+2*sz+8, icon_y+sz), (icon_x+sz+8, icon_y+2*sz)], outline=LOGO_COLOR, width=3)
+
+ draw.text((icon_x + 50, icon_y), "ANIME FLICKER", font=MEDIUM_FONT, fill=LOGO_COLOR)
+
+ # 2. Rating (Below Logo)
+ if score:
+ rating_text = f"{score/10:.1f}+ Rating"
+ draw.text((50, 130), rating_text, font=REG_FONT, fill=TEXT_COLOR)
+
+ # 3. Title (Large, Below Rating)
+ title_lines = wrap_text(title.upper(), TITLE_FONT, 600)
+ title_y = 170
+ for line in title_lines[:2]:
+ draw.text((50, title_y), line, font=TITLE_FONT, fill=TEXT_COLOR)
+ title_y += 110 # Line height
+
+ # 4. Genres (Below Title)
+ genre_text = ", ".join(genres).upper()
+ draw.text((50, title_y + 10), genre_text, font=GENRE_FONT, fill=GENRE_COLOR)
+
+ # 5. Description (Below Genres)
+ desc_y = title_y + 60
+ desc_lines = wrap_text(desc, REG_FONT, 500)
+ for line in desc_lines[:6]:
+ draw.text((50, desc_y), line, font=REG_FONT, fill=SUBTEXT_COLOR)
+ desc_y += 30
+
+ # 6. Buttons (Bottom Left)
+ btn_y = 620
+ btn_width = 160
+ btn_height = 50
+
+ # Button 1: DOWNLOAD
+ draw.rounded_rectangle((50, btn_y, 50 + btn_width, btn_y + btn_height), radius=5, fill=BUTTON_BG)
+ text_w = BOLD_FONT.getlength("DOWNLOAD")
+ text_x = 50 + (btn_width - text_w) / 2
+ draw.text((text_x, btn_y + 10), "DOWNLOAD", font=BOLD_FONT, fill=TEXT_COLOR)
+
+ # Button 2: JOIN NOW
+ btn2_x = 50 + btn_width + 40
+ draw.rounded_rectangle((btn2_x, btn_y, btn2_x + btn_width, btn_y + btn_height), radius=5, fill=BUTTON_BG)
+ text_w = BOLD_FONT.getlength("JOIN NOW")
+ text_x = btn2_x + (btn_width - text_w) / 2
+ draw.text((text_x, btn_y + 10), "JOIN NOW", font=BOLD_FONT, fill=TEXT_COLOR)
+
+
+ # 7. Right Side Honeycomb Poster
+ # Fetch poster
+ poster_resp = requests.get(poster_url)
+ poster = Image.open(BytesIO(poster_resp.content)).convert("RGBA")
+
+ # Define area
+ collage_x = 550
+ collage_width = CANVAS_WIDTH - collage_x
+ collage_height = CANVAS_HEIGHT
+
+ # Resize/Crop poster
+ aspect = poster.width / poster.height
+ target_aspect = collage_width / collage_height
+
+ if aspect > target_aspect:
+ new_height = collage_height
+ new_width = int(new_height * aspect)
+ poster = poster.resize((new_width, new_height), Image.Resampling.LANCZOS)
+ left = (new_width - collage_width) // 2
+ poster = poster.crop((left, 0, left + collage_width, new_height))
+ else:
+ new_width = collage_width
+ new_height = int(new_width / aspect)
+ poster = poster.resize((new_width, new_height), Image.Resampling.LANCZOS)
+ top = (new_height - collage_height) // 2
+ poster = poster.crop((0, top, collage_width, top + collage_height))
+
+ # Create Honeycomb Pattern
+ # We will draw the honeycombs on a mask, but also draw the outlines on an overlay.
+
+ mask = Image.new("L", (collage_width, collage_height), 0)
+ mask_draw = ImageDraw.Draw(mask)
+
+ overlay = Image.new("RGBA", (collage_width, collage_height), (0,0,0,0))
+ overlay_draw = ImageDraw.Draw(overlay)
+
+ hex_radius = 85 # Larger hexagons
+ gap = 4
+
+ dx = math.sqrt(3) * hex_radius
+ dy = 1.5 * hex_radius
+
+ cols = int(collage_width / dx) + 2
+ rows = int(collage_height / dy) + 2
+
+ # Start drawing from slightly outside to cover edges
+ for row in range(-1, rows):
+ for col in range(-1, cols):
+ cx = col * dx
+ cy = row * dy
+ if row % 2 == 1:
+ cx += dx / 2
+
+ # Center in the collage area
+ # Adjust offset to align nicely?
+
+ # Draw hexagon on mask (filled white)
+ # Make the mask slightly smaller than full size to leave room for outline?
+ # Or make the mask full size and draw outline on top?
+ # User image shows outlines BETWEEN images.
+ # So the gaps between mask hexagons should be transparent?
+ # If gap > 0, then there is space between images.
+
+ draw_regular_polygon(mask_draw, (cx, cy), hex_radius - gap, fill=255)
+
+ # Draw outline on overlay
+ # The outline should be around the hexagon.
+ # Thick outline.
+ draw_regular_polygon(overlay_draw, (cx, cy), hex_radius - gap, outline=HONEYCOMB_OUTLINE_COLOR, width=4)
+
+ # Apply mask to poster
+ poster.putalpha(mask)
+
+ # Paste poster
+ canvas.paste(poster, (collage_x, 0), poster)
+
+ # Paste overlay (outlines)
+ canvas.paste(overlay, (collage_x, 0), overlay)
+
+ # Final
+ final = BytesIO()
+ canvas.convert("RGB").save(final, "PNG")
+ final.seek(0)
+ return final
+
+@bot.message_handler(commands=['start'])
+def start(msg):
+ bot.reply_to(msg, "Anime ka naam bhej ya /thumb Haikyuu likh\nMain ekdam AnimeFlicker jaisa thumbnail bana dunga")
+
+@bot.message_handler(commands=['thumb'])
+def thumb(msg):
+ query = msg.text.replace("/thumb", "").strip()
+ if not query:
+ bot.reply_to(msg, "Bhai anime naam toh likh na!\nExample: /thumb One Piece")
+ return
+ bot.send_chat_action(msg.chat.id, 'upload_photo')
+ try:
+ resp = requests.post("https://graphql.anilist.co",
+ json={"query": """
+ query ($search: String) {
+ Media(search: $search, type: ANIME) {
+ title { romaji english }
+ coverImage { extraLarge }
+ averageScore
+ genres
+ description
+ }
+ }
+ """, "variables": {"search": query}}
+ ).json()['data']['Media']
+ if not resp:
+ bot.reply_to(msg, "Anime nahi mila bhai, sahi spelling likh")
+ return
+ img = generate_thumbnail(resp)
+ title_text = resp['title']['english'] or resp['title']['romaji']
+ bot.send_photo(msg.chat.id, img,
+ caption=f"{title_text}\n@YourChannelHere")
+ except Exception as e:
+ import traceback
+ traceback.print_exc()
+ bot.reply_to(msg, f"Error: {e}")
+
+if __name__ == "__main__":
+ print("Bot chal gaya!")
+ bot.infinity_polling()