Spaces:
Sleeping
Sleeping
import calendar | |
import logging | |
import os | |
import random | |
import textwrap | |
import time | |
import httpx | |
from icrawler.builtin import BingImageCrawler | |
from PIL import Image, ImageDraw, ImageEnhance, ImageFont, ImageOps | |
from unidecode import unidecode | |
from .formatter import format_text, limit_per_page | |
def convert_to_png(image: str) -> str: | |
output_img = f"png_{round(time.time())}.png" | |
img = Image.open(image) | |
img.save(output_img, "PNG") | |
img.close() | |
os.remove(image) | |
return output_img | |
def add_rounded_corners(img: Image.Image, radius: int = 80): | |
circle = Image.new("L", (radius * 2, radius * 2), 0) | |
draw = ImageDraw.Draw(circle) | |
draw.ellipse((0, 0, radius * 2, radius * 2), fill=255) | |
alpha = Image.new("L", img.size, 255) | |
w, h = img.size | |
alpha.paste(circle.crop((0, 0, radius, radius)), (0, 0)) | |
alpha.paste(circle.crop((radius, 0, radius * 2, radius)), (w - radius, 0)) | |
alpha.paste(circle.crop((0, radius, radius, radius * 2)), (0, h - radius)) | |
alpha.paste( | |
circle.crop((radius, radius, radius * 2, radius * 2)), (w - radius, h - radius) | |
) | |
img.putalpha(alpha) | |
return img | |
def generate_alive_image( | |
username: str, profile_pic: str, del_img: bool, font_path: str | |
) -> str: | |
if not profile_pic.endswith(".png"): | |
profile_pic = convert_to_png(profile_pic) | |
img = Image.open(profile_pic).convert("RGBA") | |
img_rotated = img.rotate(45, expand=True) | |
width, height = img_rotated.size | |
left = width / 2 - 480 / 2 | |
top = height / 2 - 480 / 2 | |
right = width / 2 + 480 / 2 | |
bottom = height / 2 + 480 / 2 | |
cropped_img = img_rotated.crop((left, top, right, bottom)) | |
img_rotated = ImageOps.fit( | |
cropped_img, (480, 480), method=0, bleed=0.0, centering=(0.5, 0.5) | |
) | |
img_rounded = add_rounded_corners(img_rotated) | |
img = img_rounded.rotate(-45, expand=True) | |
background = Image.open("./Hellbot/resources/images/hellbot_alive.png").convert( | |
"RGBA" | |
) | |
background.paste(img, (383, 445), img) | |
draw = ImageDraw.Draw(background) | |
text = format_text(username[:25] + ("..." if len(username) > 25 else "")) | |
font_size = width // 15 | |
font = ImageFont.truetype(font_path, font_size, encoding="utf-8") | |
text_length = draw.textlength(text, font) | |
position = ((background.width - text_length) / 2, background.height - 145) | |
draw.text( | |
position, | |
unidecode(text), | |
(255, 255, 255), | |
font, | |
) | |
output_img = f"alive_{int(time.time())}.png" | |
background.save(output_img, "PNG") | |
background.close() | |
if del_img: | |
os.remove(profile_pic) | |
return output_img | |
async def get_wallpapers( | |
access: str, | |
limit: int, | |
query: str = "", | |
isRandom: bool = False, | |
) -> list[str]: | |
headers = {"Authorization": f"Client-ID {access}"} | |
if isRandom: | |
api = f"https://api.unsplash.com/photos/random?count={limit}" | |
response = httpx.get(api, headers=headers) | |
results = response.json() | |
urls = [i["urls"]["raw"] for i in results] | |
else: | |
api = f"https://api.unsplash.com/search/photos?query={query}&page={limit_per_page(limit)}" | |
response = httpx.get(api, headers=headers) | |
result = response.json() | |
urls = [i["urls"]["raw"] for i in result["results"]] | |
random.shuffle(urls) | |
return urls[:limit] | |
async def deep_fry(img: Image.Image) -> Image.Image: | |
colours = ( | |
(random.randint(50, 200), random.randint(40, 170), random.randint(40, 190)), | |
(random.randint(190, 255), random.randint(170, 240), random.randint(180, 250)), | |
) | |
img = img.copy().convert("RGB") | |
img = img.convert("RGB") | |
width, height = img.width, img.height | |
img = img.resize( | |
( | |
int(width ** random.uniform(0.8, 0.9)), | |
int(height ** random.uniform(0.8, 0.9)), | |
), | |
resample=Image.LANCZOS, | |
) | |
img = img.resize( | |
( | |
int(width ** random.uniform(0.85, 0.95)), | |
int(height ** random.uniform(0.85, 0.95)), | |
), | |
resample=Image.BILINEAR, | |
) | |
img = img.resize( | |
( | |
int(width ** random.uniform(0.89, 0.98)), | |
int(height ** random.uniform(0.89, 0.98)), | |
), | |
resample=Image.BICUBIC, | |
) | |
img = img.resize((width, height), resample=Image.BICUBIC) | |
img = ImageOps.posterize(img, random.randint(3, 7)) | |
overlay = img.split()[0] | |
overlay = ImageEnhance.Contrast(overlay).enhance(random.uniform(1.0, 2.0)) | |
overlay = ImageEnhance.Brightness(overlay).enhance(random.uniform(1.0, 2.0)) | |
overlay = ImageOps.colorize(overlay, colours[0], colours[1]) | |
img = Image.blend(img, overlay, random.uniform(0.1, 0.4)) | |
img = ImageEnhance.Sharpness(img).enhance(random.randint(5, 300)) | |
return img | |
async def make_logo(background: str, text: str, font_path: str) -> str: | |
if not background.endswith(".png"): | |
background = convert_to_png(background) | |
bg = Image.open(background).convert("RGBA") | |
bgWidth, bgHeight = bg.size | |
text = format_text(text) | |
font_size = bgWidth // len(text) | |
font = ImageFont.truetype(font_path, font_size, encoding="utf-8") | |
draw = ImageDraw.Draw(bg) | |
text_length = draw.textlength(text, font) | |
x = (bgWidth - text_length) // 2 | |
y = (bgHeight - font_size) // 2 | |
draw.text( | |
(x, y), | |
unidecode(text), | |
(255, 255, 255), | |
font, | |
stroke_fill=(0, 0, 0), | |
stroke_width=2, | |
) | |
output_img = f"logo_{int(time.time())}.png" | |
bg.save(output_img, "PNG") | |
bg.close() | |
os.remove(background) | |
return output_img | |
async def draw_meme( | |
image_path: str, upper_text: str = "", lower_text: str = "" | |
) -> list[str]: | |
image = Image.open(image_path) | |
width, height = image.size | |
draw = ImageDraw.Draw(image) | |
font_size = int((30 / 500) * width) | |
font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", font_size) | |
curr_height, padding = 20, 5 | |
for utext in textwrap.wrap(upper_text, 25): | |
upper_width = draw.textlength(utext, font=font) | |
draw.text( | |
((width - upper_width) / 2, curr_height), | |
unidecode(utext), | |
(255, 255, 255), | |
font, | |
stroke_width=3, | |
stroke_fill=(0, 0, 0), | |
) | |
curr_height += font_size + padding | |
curr_height = height - font_size | |
for ltext in reversed(textwrap.wrap(lower_text, 25)): | |
lower_width = draw.textlength(ltext, font=font) | |
draw.text( | |
((width - lower_width) / 2, curr_height - font_size), | |
ltext, | |
(255, 255, 255), | |
font, | |
stroke_width=3, | |
stroke_fill=(0, 0, 0), | |
) | |
curr_height -= font_size + padding | |
filename = f"meme_{int(time.time())}" | |
image.save(f"{filename}.png", "PNG", optimize=True) | |
image.save(f"{filename}.webp", "WEBP", optimize=True) | |
image.close() | |
return [f"{filename}.png", f"{filename}.webp"] | |
async def remove_bg(api_key: str, image: str) -> str: | |
response = httpx.post( | |
"https://api.remove.bg/v1.0/removebg", | |
files={"image_file": open(image, "rb")}, | |
data={"size": "auto"}, | |
headers={"X-Api-Key": api_key}, | |
) | |
filename = f"removedbg_{int(time.time())}.png" | |
if response.is_success: | |
with open(filename, "wb") as f: | |
f.write(response.content) | |
else: | |
raise Exception( | |
f"RemoveBGError: [{response.status_code}] {response.content.decode('utf-8')}" | |
) | |
return filename | |
def create_gradient( | |
size: tuple[int, int], | |
color_start: tuple[int, int, int], | |
color_end: tuple[int, int, int], | |
) -> Image.Image: | |
gradient = Image.new("RGB", (size)) | |
draw = ImageDraw.Draw(gradient) | |
for x in range(size[0]): | |
r = int(color_start[0] + (color_end[0] - color_start[0]) * (x / size[0])) | |
g = int(color_start[1] + (color_end[1] - color_start[1]) * (x / size[0])) | |
b = int(color_start[2] + (color_end[2] - color_start[2]) * (x / size[0])) | |
draw.line([(x, 0), (x, size[1])], fill=(r, g, b)) | |
return gradient | |
async def create_calendar(year: int, month: int) -> str: | |
cal = calendar.monthcalendar(year, month) | |
month_name = calendar.month_name[month] | |
calendar_image = create_gradient((500, 500), (140, 200, 250), (0, 150, 200)) | |
draw = ImageDraw.Draw(calendar_image) | |
month_font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 40) | |
month_x = ( | |
calendar_image.width - draw.textlength(f"{month_name} {year}", month_font) | |
) // 2 | |
month_y = 30 | |
draw.text( | |
(month_x, month_y), | |
f"{month_name} {year}", | |
(43, 255, 136), | |
month_font, | |
stroke_width=2, | |
stroke_fill=(255, 40, 40), | |
) | |
week_font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 23) | |
weekdays_text = " ".join([day[:3] for day in calendar.day_name]) | |
textsize = draw.textlength(weekdays_text, week_font) | |
draw.text( | |
((calendar_image.width - textsize) // 2, month_y + 80), | |
weekdays_text, | |
(150, 190, 200), | |
week_font, | |
stroke_width=2, | |
stroke_fill=(200, 150, 250), | |
) | |
scale_factor = 1.5 | |
cell_size = 30 | |
padding = 15 | |
font = ImageFont.truetype("./Hellbot/resources/fonts/Montserrat.ttf", 30) | |
for week_num, week in enumerate(cal): | |
for day_num, day in enumerate(week): | |
x = int(day_num * (cell_size + padding) * scale_factor) | |
y = int((week_num + 3) * (cell_size + padding) * scale_factor) | |
cell_width = int(cell_size * scale_factor) | |
cell_height = int(cell_size * scale_factor) | |
text_x = ( | |
int(x + (cell_width - draw.textlength(str(day), font=font)) // 2) | |
+ cell_size | |
) | |
text_y = ( | |
int(y + (cell_height - draw.textlength(str(day), font=font)) // 2) - 55 | |
) | |
if day != 0: | |
draw.text( | |
(text_x, text_y), | |
str(day), | |
(240, 200, 100), | |
font, | |
stroke_width=1, | |
stroke_fill=(0, 0, 0), | |
) | |
filename = f"calendar_{int(time.time())}.png" | |
calendar_image.save(filename, "PNG") | |
calendar_image.close() | |
return filename | |
async def create_thumbnail(photo: str, xy: tuple[int, int], file_size: int): | |
img = Image.open(photo) | |
img.thumbnail(xy) | |
size_in_bytes = file_size * 1024 | |
quality = 90 | |
while True: | |
img.save(photo, "JPEG", quality=quality, optimize=True) | |
if os.path.getsize(photo) <= size_in_bytes: | |
break | |
quality -= 5 | |
return photo | |
async def download_images(query: str, limit: int) -> list[str]: | |
offset = random.randint(0, 20) | |
crawler = BingImageCrawler(log_level=logging.ERROR) | |
crawler.crawl(query, offset=offset, max_num=limit) | |
return [os.path.join("images", image) for image in os.listdir("images")] | |