mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-01 23:35:29 +00:00
[AB-70] moving image generation functionality to separate image generation module
removing doge image generation from default base split rest-api into separate modules (base and extensions)
This commit is contained in:
3
python/components/image-gen/docker/Dockerfile
Normal file
3
python/components/image-gen/docker/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM alpine:3.19.0
|
||||
ADD resources /python/resources
|
||||
ADD python /python
|
||||
76
python/components/image-gen/python/endpoints/triggered.py
Normal file
76
python/components/image-gen/python/endpoints/triggered.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from __main__ import app
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
from flask import request
|
||||
import requests
|
||||
import validators
|
||||
import logging
|
||||
import io
|
||||
|
||||
from utils import flask_utils
|
||||
|
||||
|
||||
allowed_content_length = 4_000_000
|
||||
allowed_image_formats = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']
|
||||
max_off_set = 5
|
||||
gif_speedup_factor = 4
|
||||
|
||||
|
||||
@app.route('/memes/triggered/file.gif') # to directly embed, discord _requires_ this file ending, it seems
|
||||
def triggered_animated():
|
||||
url = request.args.get('url')
|
||||
if not validators.url(url):
|
||||
return 'no valid url', 400
|
||||
session = requests.Session()
|
||||
response = session.head(url)
|
||||
content_type = response.headers['content-type']
|
||||
|
||||
if content_type not in allowed_image_formats:
|
||||
return f'Incorrect image type {content_type}', 400
|
||||
|
||||
actual_content_length = int(response.headers['content-length'])
|
||||
if actual_content_length > allowed_content_length:
|
||||
return f'Image too large {actual_content_length}', 400
|
||||
|
||||
image_file = requests.get(url, stream=True)
|
||||
input_image = Image.open(io.BytesIO(image_file.content))
|
||||
original_input_image = input_image
|
||||
old_width, old_height = input_image.size
|
||||
with Image.open('resources/img/triggered_footer.jpg') as footer_image:
|
||||
footer_width, footer_height = footer_image.size
|
||||
footer_ratio = old_width / footer_width
|
||||
desired_new_footer_width = int(footer_width * footer_ratio)
|
||||
if footer_ratio > 1:
|
||||
footer_image = footer_image.resize((desired_new_footer_width, footer_height))
|
||||
else:
|
||||
footer_image = ImageOps.contain(footer_image, (desired_new_footer_width, footer_height))
|
||||
new_footer_width, new_footer_height = footer_image.size
|
||||
new_total_height = old_height + new_footer_height
|
||||
points = [[0, -max_off_set], [max_off_set, 0], [0, max_off_set], [-max_off_set, 0]]
|
||||
if content_type == 'image/gif':
|
||||
logging.info(f'Rendering triggered for gif.')
|
||||
frame_count = original_input_image.n_frames
|
||||
old_frames = []
|
||||
for frame_index in range(frame_count):
|
||||
input_image.seek(frame_index)
|
||||
frame = input_image.convert('RGBA')
|
||||
old_frames.append(frame)
|
||||
frames = []
|
||||
for index, old_frame in enumerate(old_frames):
|
||||
off_set = points[index % len(points)]
|
||||
frame = Image.new('RGBA', (old_width, new_total_height), (0, 0, 0, 0))
|
||||
old_frame = ImageOps.contain(old_frame, (old_width + max_off_set * 2, old_height + max_off_set * 2))
|
||||
frame.paste(old_frame, (-max_off_set + off_set[0], -max_off_set + off_set[1]))
|
||||
frame.paste(footer_image, (0, old_height))
|
||||
frames.append(frame)
|
||||
return flask_utils.serve_pil_gif_image(frames, (int(original_input_image.info['duration']) / gif_speedup_factor))
|
||||
else:
|
||||
input_image = ImageOps.contain(input_image, (old_width + max_off_set * 2, old_height + max_off_set * 2))
|
||||
frames = []
|
||||
logging.info(f'Rendering triggered for static image.')
|
||||
for off_set in points:
|
||||
frame = Image.new('RGBA', (old_width, new_total_height), (0, 0, 0, 0))
|
||||
frame.paste(input_image, (-max_off_set + off_set[0], -max_off_set + off_set[1]))
|
||||
frame.paste(footer_image, (0, old_height))
|
||||
frames.append(frame)
|
||||
return flask_utils.serve_pil_gif_image(frames)
|
||||
BIN
python/components/image-gen/resources/img/triggered_footer.jpg
Normal file
BIN
python/components/image-gen/resources/img/triggered_footer.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -5,6 +5,5 @@ RUN apk --no-cache add msttcorefonts-installer fontconfig && \
|
||||
ADD wrapper /
|
||||
ADD python/requirements.txt requirements.txt
|
||||
RUN pip install -r requirements.txt
|
||||
ADD resources /python/resources
|
||||
ADD python /python
|
||||
CMD ["/run.sh"]
|
||||
@@ -11,8 +11,6 @@ app = Flask(__name__, template_folder=template_dir)
|
||||
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
# loads the api end points
|
||||
from base import image_gen
|
||||
|
||||
# This code was only done, because "from custom import *" did not work, it seems it did not execute the code in it
|
||||
# while the code was valid
|
||||
@@ -48,7 +46,7 @@ def dynamic_import_from_src(src, star_import=False):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
dynamic_import_from_src("custom", star_import=False)
|
||||
dynamic_import_from_src("endpoints", star_import=False)
|
||||
|
||||
@app.route('/')
|
||||
def hello():
|
||||
@@ -14,4 +14,5 @@ urllib3==2.0.7
|
||||
waitress==2.1.2
|
||||
Werkzeug==3.0.1
|
||||
zipp==3.17.0
|
||||
pytz==2023.3.post1
|
||||
pytz==2023.3.post1
|
||||
validators==0.22.0
|
||||
@@ -10,6 +10,13 @@ def serve_pil_image(pil_img):
|
||||
return send_file(img_io, mimetype='image/png')
|
||||
|
||||
|
||||
def serve_pil_gif_image(frames, duration=25):
|
||||
animated_gif = BytesIO()
|
||||
frames[0].save(animated_gif, format='GIF', save_all=True, append_images=frames[1:], duration=duration, loop=0, disposal=2)
|
||||
animated_gif.seek(0)
|
||||
return send_file(animated_gif, mimetype='image/gif')
|
||||
|
||||
|
||||
class ValidationException(Exception):
|
||||
def __init__(self, provided_value, message):
|
||||
self.provided_value = provided_value
|
||||
@@ -1,18 +0,0 @@
|
||||
from __main__ import app
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from flask import request
|
||||
|
||||
from utils import flask_utils
|
||||
|
||||
@app.route('/memes/doge/orangeSun/')
|
||||
def image_gen():
|
||||
text = request.args.get('text')
|
||||
with Image.open('resources/img/semf_template.jpg') as im:
|
||||
d1 = ImageDraw.Draw(im)
|
||||
text_box_size = (300, 240)
|
||||
W, H = text_box_size
|
||||
font = ImageFont.truetype(f'Impact.ttf', 60)
|
||||
_, _, w, h = d1.textbbox((0, 0), text, font=font)
|
||||
d1.text(((W-w)/2 + 320, (H-h)/2 + 120), text, font=font, fill=(255, 255, 255))
|
||||
return flask_utils.serve_pil_image(im)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Reference in New Issue
Block a user