|
import random |
|
import logging |
|
import os |
|
import re |
|
import aiofiles |
|
import aiohttp |
|
from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont |
|
from youtubesearchpython.__future__ import VideosSearch |
|
import tempfile |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
|
def changeImageSize(maxWidth, maxHeight, image): |
|
widthRatio = maxWidth / image.size[0] |
|
heightRatio = maxHeight / image.size[1] |
|
newWidth = int(widthRatio * image.size[0]) |
|
newHeight = int(heightRatio * image.size[1]) |
|
newImage = image.resize((newWidth, newHeight)) |
|
return newImage |
|
|
|
def truncate(text): |
|
list = text.split(" ") |
|
text1 = "" |
|
text2 = "" |
|
for i in list: |
|
if len(text1) + len(i) < 30: |
|
text1 += " " + i |
|
elif len(text2) + len(i) < 30: |
|
text2 += " " + i |
|
|
|
text1 = text1.strip() |
|
text2 = text2.strip() |
|
return [text1,text2] |
|
|
|
def random_color(): |
|
return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) |
|
|
|
def generate_gradient(width, height, start_color, end_color): |
|
base = Image.new('RGBA', (width, height), start_color) |
|
top = Image.new('RGBA', (width, height), end_color) |
|
mask = Image.new('L', (width, height)) |
|
mask_data = [] |
|
for y in range(height): |
|
mask_data.extend([int(60 * (y / height))] * width) |
|
mask.putdata(mask_data) |
|
base.paste(top, (0, 0), mask) |
|
return base |
|
|
|
def add_border(image, border_width, border_color): |
|
width, height = image.size |
|
new_width = width + 2 * border_width |
|
new_height = height + 2 * border_width |
|
new_image = Image.new("RGBA", (new_width, new_height), border_color) |
|
new_image.paste(image, (border_width, border_width)) |
|
return new_image |
|
|
|
def crop_center_circle(img, output_size, border, border_color, crop_scale=1.5): |
|
half_the_width = img.size[0] / 2 |
|
half_the_height = img.size[1] / 2 |
|
larger_size = int(output_size * crop_scale) |
|
img = img.crop( |
|
( |
|
half_the_width - larger_size/2, |
|
half_the_height - larger_size/2, |
|
half_the_width + larger_size/2, |
|
half_the_height + larger_size/2 |
|
) |
|
) |
|
|
|
img = img.resize((output_size - 2*border, output_size - 2*border)) |
|
|
|
|
|
final_img = Image.new("RGBA", (output_size, output_size), border_color) |
|
|
|
|
|
mask_main = Image.new("L", (output_size - 2*border, output_size - 2*border), 0) |
|
draw_main = ImageDraw.Draw(mask_main) |
|
draw_main.ellipse((0, 0, output_size - 2*border, output_size - 2*border), fill=255) |
|
|
|
final_img.paste(img, (border, border), mask_main) |
|
|
|
|
|
mask_border = Image.new("L", (output_size, output_size), 0) |
|
draw_border = ImageDraw.Draw(mask_border) |
|
draw_border.ellipse((0, 0, output_size, output_size), fill=255) |
|
|
|
result = Image.composite(final_img, Image.new("RGBA", final_img.size, (0, 0, 0, 0)), mask_border) |
|
|
|
return result |
|
|
|
def draw_text_with_shadow(background, draw, position, text, font, fill, shadow_offset=(3, 3), shadow_blur=5): |
|
|
|
shadow = Image.new('RGBA', background.size, (0, 0, 0, 0)) |
|
shadow_draw = ImageDraw.Draw(shadow) |
|
|
|
|
|
shadow_draw.text(position, text, font=font, fill="black") |
|
|
|
|
|
shadow = shadow.filter(ImageFilter.GaussianBlur(radius=shadow_blur)) |
|
|
|
|
|
background.paste(shadow, shadow_offset, shadow) |
|
|
|
|
|
draw.text(position, text, font=font, fill=fill) |
|
|
|
async def gen_thumb(videoid: str): |
|
try: |
|
temp_dir = tempfile.gettempdir() |
|
if os.path.isfile(os.path.join(temp_dir, f"{videoid}_v4.png")): |
|
return os.path.join(temp_dir, f"{videoid}_v4.png") |
|
|
|
url = f"https://www.youtube.com/watch?v={videoid}" |
|
results = VideosSearch(url, limit=1) |
|
for result in (await results.next())["result"]: |
|
title = result.get("title") |
|
if title: |
|
title = re.sub("\W+", " ", title).title() |
|
else: |
|
title = "Unsupported Title" |
|
duration = result.get("duration") |
|
if not duration: |
|
duration = "Live" |
|
thumbnail_data = result.get("thumbnails") |
|
if thumbnail_data: |
|
thumbnail = thumbnail_data[0]["url"].split("?")[0] |
|
else: |
|
thumbnail = None |
|
views_data = result.get("viewCount") |
|
if views_data: |
|
views = views_data.get("short") |
|
if not views: |
|
views = "Unknown Views" |
|
else: |
|
views = "Unknown Views" |
|
channel_data = result.get("channel") |
|
if channel_data: |
|
channel = channel_data.get("name") |
|
if not channel: |
|
channel = "Unknown Channel" |
|
else: |
|
channel = "Unknown Channel" |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
async with session.get(thumbnail) as resp: |
|
|
|
content = await resp.read() |
|
if resp.status == 200: |
|
content_type = resp.headers.get('Content-Type') |
|
if 'jpeg' in content_type or 'jpg' in content_type: |
|
extension = 'jpg' |
|
elif 'png' in content_type: |
|
extension = 'png' |
|
else: |
|
logging.error(f"Unexpected content type: {content_type}") |
|
return None |
|
|
|
filepath = os.path.join(temp_dir, f"thumb{videoid}.png") |
|
f = await aiofiles.open(filepath, mode="wb") |
|
await f.write(await resp.read()) |
|
await f.close() |
|
|
|
|
|
|
|
image_path = os.path.join(temp_dir, f"thumb{videoid}.png") |
|
youtube = Image.open(image_path) |
|
image1 = changeImageSize(1280, 720, youtube) |
|
|
|
image2 = image1.convert("RGBA") |
|
background = image2.filter(filter=ImageFilter.BoxBlur(20)) |
|
enhancer = ImageEnhance.Brightness(background) |
|
background = enhancer.enhance(0.6) |
|
|
|
|
|
start_gradient_color = random_color() |
|
end_gradient_color = random_color() |
|
gradient_image = generate_gradient(1280, 720, start_gradient_color, end_gradient_color) |
|
background = Image.blend(background, gradient_image, alpha=0.2) |
|
|
|
draw = ImageDraw.Draw(background) |
|
arial = ImageFont.truetype("DragMusic/assets/font2.ttf", 30) |
|
font = ImageFont.truetype("DragMusic/assets/font.ttf", 30) |
|
title_font = ImageFont.truetype("DragMusic/assets/font3.ttf", 45) |
|
|
|
|
|
circle_thumbnail = crop_center_circle(youtube, 400, 20, start_gradient_color) |
|
circle_thumbnail = circle_thumbnail.resize((400, 400)) |
|
circle_position = (120, 160) |
|
background.paste(circle_thumbnail, circle_position, circle_thumbnail) |
|
|
|
text_x_position = 565 |
|
title1 = truncate(title) |
|
draw_text_with_shadow(background, draw, (text_x_position, 180), title1[0], title_font, (255, 255, 255)) |
|
draw_text_with_shadow(background, draw, (text_x_position, 230), title1[1], title_font, (255, 255, 255)) |
|
draw_text_with_shadow(background, draw, (text_x_position, 320), f"{channel} | {views[:23]}", arial, (255, 255, 255)) |
|
|
|
|
|
line_length = 580 |
|
line_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) |
|
|
|
if duration != "Live": |
|
color_line_percentage = random.uniform(0.15, 0.85) |
|
color_line_length = int(line_length * color_line_percentage) |
|
white_line_length = line_length - color_line_length |
|
|
|
start_point_color = (text_x_position, 380) |
|
end_point_color = (text_x_position + color_line_length, 380) |
|
draw.line([start_point_color, end_point_color], fill=line_color, width=9) |
|
|
|
start_point_white = (text_x_position + color_line_length, 380) |
|
end_point_white = (text_x_position + line_length, 380) |
|
draw.line([start_point_white, end_point_white], fill="white", width=8) |
|
|
|
circle_radius = 10 |
|
circle_position = (end_point_color[0], end_point_color[1]) |
|
draw.ellipse([circle_position[0] - circle_radius, circle_position[1] - circle_radius, |
|
circle_position[0] + circle_radius, circle_position[1] + circle_radius], fill=line_color) |
|
|
|
else: |
|
line_color = (255, 0, 0) |
|
start_point_color = (text_x_position, 380) |
|
end_point_color = (text_x_position + line_length, 380) |
|
draw.line([start_point_color, end_point_color], fill=line_color, width=9) |
|
|
|
circle_radius = 10 |
|
circle_position = (end_point_color[0], end_point_color[1]) |
|
draw.ellipse([circle_position[0] - circle_radius, circle_position[1] - circle_radius, |
|
circle_position[0] + circle_radius, circle_position[1] + circle_radius], fill=line_color) |
|
|
|
draw_text_with_shadow(background, draw, (text_x_position, 400), "00:00", arial, (255, 255, 255)) |
|
draw_text_with_shadow(background, draw, (1080, 400), duration, arial, (255, 255, 255)) |
|
|
|
play_icons = Image.open("DragMusic/assets/play_icons.png") |
|
play_icons = play_icons.resize((580, 62)) |
|
background.paste(play_icons, (text_x_position, 450), play_icons) |
|
|
|
os.remove(os.path.join(temp_dir, f"thumb{videoid}.png")) |
|
|
|
background_path = os.path.join(temp_dir, f"{videoid}_v4.png") |
|
background.save(background_path) |
|
|
|
return background_path |
|
|
|
except Exception as e: |
|
logging.error(f"Error generating thumbnail for video {videoid}: {e}") |
|
traceback.print_exc() |
|
return None |