This commit is contained in:
phamhungd 2025-11-24 16:18:23 +07:00
parent 0e12214f60
commit cd6dcd6863
2 changed files with 149 additions and 39 deletions

BIN
.DS_Store vendored

Binary file not shown.

182
app.py
View file

@ -3,6 +3,7 @@ import base64
import uuid import uuid
import glob import glob
import json import json
import shutil
from datetime import datetime from datetime import datetime
from io import BytesIO from io import BytesIO
from send2trash import send2trash from send2trash import send2trash
@ -14,6 +15,95 @@ from PIL import Image, PngImagePlugin
app = Flask(__name__) app = Flask(__name__)
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
PREVIEW_MAX_DIMENSION = 1024
PREVIEW_JPEG_QUALITY = 85
try:
RESAMPLE_FILTER = Image.Resampling.LANCZOS
except AttributeError:
if hasattr(Image, 'LANCZOS'):
RESAMPLE_FILTER = Image.LANCZOS
else:
RESAMPLE_FILTER = Image.BICUBIC
FORMAT_BY_EXTENSION = {
'.jpg': 'JPEG',
'.jpeg': 'JPEG',
'.png': 'PNG',
'.webp': 'WEBP',
}
def _normalize_extension(ext):
if not ext:
return '.png'
ext = ext.lower()
if not ext.startswith('.'):
ext = f'.{ext}'
return ext
def _format_for_extension(ext):
return FORMAT_BY_EXTENSION.get(ext, 'PNG')
def save_compressed_preview(image, filepath, extension):
extension = _normalize_extension(extension)
image_copy = image.copy()
image_copy.thumbnail((PREVIEW_MAX_DIMENSION, PREVIEW_MAX_DIMENSION), RESAMPLE_FILTER)
image_format = _format_for_extension(extension)
save_kwargs = {}
if image_format == 'JPEG':
if image_copy.mode not in ('RGB', 'RGBA'):
image_copy = image_copy.convert('RGB')
save_kwargs.update(quality=PREVIEW_JPEG_QUALITY, optimize=True, progressive=True)
elif image_format == 'WEBP':
save_kwargs.update(quality=PREVIEW_JPEG_QUALITY, method=6)
elif image_format == 'PNG':
save_kwargs.update(optimize=True)
image_copy.save(filepath, format=image_format, **save_kwargs)
def save_preview_image(preview_dir, extension='.png', source_bytes=None, source_path=None):
extension = _normalize_extension(extension)
filename = f"template_{uuid.uuid4()}{extension}"
filepath = os.path.join(preview_dir, filename)
try:
image = None
if source_bytes is not None:
image = Image.open(BytesIO(source_bytes))
elif source_path is not None:
image = Image.open(source_path)
if image is not None:
save_compressed_preview(image, filepath, extension)
return filename
elif source_bytes is not None:
with open(filepath, 'wb') as f:
f.write(source_bytes)
return filename
elif source_path is not None:
shutil.copy2(source_path, filepath)
return filename
except Exception as exc:
print(f"Error saving preview image '{filename}': {exc}")
try:
if source_bytes is not None:
with open(filepath, 'wb') as f:
f.write(source_bytes)
return filename
if source_path is not None:
shutil.copy2(source_path, filepath)
return filename
except Exception as fallback_exc:
print(f"Fallback saving preview image failed: {fallback_exc}")
return None
return None
FAVORITES_FILE = os.path.join(os.path.dirname(__file__), 'template_favorites.json') FAVORITES_FILE = os.path.join(os.path.dirname(__file__), 'template_favorites.json')
def load_template_favorites(): def load_template_favorites():
@ -399,15 +489,17 @@ def save_template():
if 'preview' in request.files: if 'preview' in request.files:
file = request.files['preview'] file = request.files['preview']
if file.filename: if file.filename:
ext = os.path.splitext(file.filename)[1] ext = os.path.splitext(file.filename)[1] or '.png'
if not ext: file.stream.seek(0)
ext = '.png' file_bytes = file.read()
preview_filename = save_preview_image(
preview_dir=preview_dir,
extension=ext,
source_bytes=file_bytes
)
filename = f"template_{uuid.uuid4()}{ext}" if preview_filename:
filepath = os.path.join(preview_dir, filename) preview_path = url_for('static', filename=f'preview/{preview_filename}')
file.save(filepath)
preview_path = url_for('static', filename=f'preview/{filename}')
# If no file uploaded, check if URL/path provided # If no file uploaded, check if URL/path provided
if not preview_path: if not preview_path:
@ -433,13 +525,16 @@ def save_template():
parsed = urlparse(preview_url) parsed = urlparse(preview_url)
ext = os.path.splitext(parsed.path)[1] or '.png' ext = os.path.splitext(parsed.path)[1] or '.png'
filename = f"template_{uuid.uuid4()}{ext}" preview_filename = save_preview_image(
filepath = os.path.join(preview_dir, filename) preview_dir=preview_dir,
extension=ext,
source_bytes=response.content
)
with open(filepath, 'wb') as f: if preview_filename:
f.write(response.content) preview_path = url_for('static', filename=f'preview/{preview_filename}')
else:
preview_path = url_for('static', filename=f'preview/{filename}') preview_path = preview_url
elif preview_url.startswith('/static/'): elif preview_url.startswith('/static/'):
# Local path - copy to preview folder # Local path - copy to preview folder
@ -448,13 +543,16 @@ def save_template():
if os.path.exists(source_path): if os.path.exists(source_path):
ext = os.path.splitext(source_path)[1] or '.png' ext = os.path.splitext(source_path)[1] or '.png'
filename = f"template_{uuid.uuid4()}{ext}" preview_filename = save_preview_image(
dest_path = os.path.join(preview_dir, filename) preview_dir=preview_dir,
extension=ext,
source_path=source_path
)
import shutil if preview_filename:
shutil.copy2(source_path, dest_path) preview_path = url_for('static', filename=f'preview/{preview_filename}')
else:
preview_path = url_for('static', filename=f'preview/{filename}') preview_path = preview_url
else: else:
# File doesn't exist, use original path # File doesn't exist, use original path
preview_path = preview_url preview_path = preview_url
@ -543,10 +641,16 @@ def update_template():
file = request.files['preview'] file = request.files['preview']
if file.filename: if file.filename:
ext = os.path.splitext(file.filename)[1] or '.png' ext = os.path.splitext(file.filename)[1] or '.png'
filename = f"template_{uuid.uuid4()}{ext}" file.stream.seek(0)
filepath = os.path.join(preview_dir, filename) file_bytes = file.read()
file.save(filepath) preview_filename = save_preview_image(
preview_path = url_for('static', filename=f'preview/{filename}') preview_dir=preview_dir,
extension=ext,
source_bytes=file_bytes
)
if preview_filename:
preview_path = url_for('static', filename=f'preview/{preview_filename}')
if not preview_path: if not preview_path:
preview_url = request.form.get('preview_path') preview_url = request.form.get('preview_path')
@ -567,13 +671,16 @@ def update_template():
parsed = urlparse(preview_url) parsed = urlparse(preview_url)
ext = os.path.splitext(parsed.path)[1] or '.png' ext = os.path.splitext(parsed.path)[1] or '.png'
filename = f"template_{uuid.uuid4()}{ext}" preview_filename = save_preview_image(
filepath = os.path.join(preview_dir, filename) preview_dir=preview_dir,
extension=ext,
source_bytes=response.content
)
with open(filepath, 'wb') as f: if preview_filename:
f.write(response.content) preview_path = url_for('static', filename=f'preview/{preview_filename}')
else:
preview_path = url_for('static', filename=f'preview/{filename}') preview_path = preview_url
elif preview_url.startswith('/static/'): elif preview_url.startswith('/static/'):
rel_path = preview_url.split('/static/')[1] rel_path = preview_url.split('/static/')[1]
@ -581,13 +688,16 @@ def update_template():
if os.path.exists(source_path): if os.path.exists(source_path):
ext = os.path.splitext(source_path)[1] or '.png' ext = os.path.splitext(source_path)[1] or '.png'
filename = f"template_{uuid.uuid4()}{ext}" preview_filename = save_preview_image(
dest_path = os.path.join(preview_dir, filename) preview_dir=preview_dir,
extension=ext,
source_path=source_path
)
import shutil if preview_filename:
shutil.copy2(source_path, dest_path) preview_path = url_for('static', filename=f'preview/{preview_filename}')
else:
preview_path = url_for('static', filename=f'preview/{filename}') preview_path = preview_url
else: else:
preview_path = preview_url preview_path = preview_url
else: else: