Spaces:
Running
Running
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 |