xyphron / DragMusic /utils /thumbnails.py
taslim19
added cache again
93fc186
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()
# os.system(f"file {filepath}")
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