Spaces:
Sleeping
Sleeping
File size: 6,036 Bytes
8b1c497 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | import os
import zipfile
import io
from flask import Flask, render_template, request, send_file, after_this_request
from PIL import Image, ImageDraw, ImageFont, ImageOps
import tempfile
import shutil
import time
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50MB limit
def process_image(img, options):
# 1. Strip EXIF (by creating a new image)
if options.get('strip_exif'):
data = list(img.getdata())
image_without_exif = Image.new(img.mode, img.size)
image_without_exif.putdata(data)
img = image_without_exif
# 2. Resize
resize_mode = options.get('resize_mode')
if resize_mode == 'percentage':
scale = int(options.get('resize_value', 100)) / 100.0
if scale != 1.0:
new_size = (int(img.width * scale), int(img.height * scale))
img = img.resize(new_size, Image.Resampling.LANCZOS)
elif resize_mode == 'width':
target_width = int(options.get('resize_value', img.width))
if target_width != img.width:
ratio = target_width / img.width
new_size = (target_width, int(img.height * ratio))
img = img.resize(new_size, Image.Resampling.LANCZOS)
# 3. Watermark
watermark_text = options.get('watermark_text')
if watermark_text:
# Create a transparent layer
txt_layer = Image.new('RGBA', img.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(txt_layer)
# Calculate font size (relative to image width)
fontsize = int(img.width / 20)
if fontsize < 10: fontsize = 10
try:
# Try to load a default font, otherwise load default
# On Linux/Docker, paths might differ.
# We'll use default bitmap font if TTF not found or just try-except
font = ImageFont.truetype("DejaVuSans.ttf", fontsize)
except IOError:
font = ImageFont.load_default()
# Position: Bottom Right with padding
# Get text size
bbox = draw.textbbox((0, 0), watermark_text, font=font)
textwidth = bbox[2] - bbox[0]
textheight = bbox[3] - bbox[1]
padding = 10
x = img.width - textwidth - padding
y = img.height - textheight - padding
# Draw semi-transparent text
draw.text((x, y), watermark_text, font=font, fill=(255, 255, 255, 128))
if img.mode != 'RGBA':
img = img.convert('RGBA')
img = Image.alpha_composite(img, txt_layer)
# 4. Format Conversion handled during save
return img
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
@app.route('/process', methods=['POST'])
def process():
if 'files' not in request.files:
return "No files uploaded", 400
files = request.files.getlist('files')
if not files or files[0].filename == '':
return "No files selected", 400
# Get options
target_format = request.form.get('target_format', 'original')
resize_mode = request.form.get('resize_mode', 'none') # none, percentage, width
resize_value = request.form.get('resize_value', 0)
watermark_text = request.form.get('watermark_text', '').strip()
strip_exif = 'strip_exif' in request.form
options = {
'resize_mode': resize_mode,
'resize_value': resize_value,
'watermark_text': watermark_text,
'strip_exif': strip_exif
}
# Create a temporary directory for processing
temp_dir = tempfile.mkdtemp()
try:
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for file in files:
if not file.filename:
continue
try:
img = Image.open(file.stream)
# Process
img = process_image(img, options)
# Determine output filename and format
original_name = os.path.splitext(file.filename)[0]
ext = os.path.splitext(file.filename)[1].lower()
if target_format != 'original':
ext = '.' + target_format.lower()
if target_format.lower() == 'jpeg':
img = img.convert('RGB')
elif target_format.lower() == 'png':
# Keep RGBA if possible, or convert if needed
pass
# Save to temp buffer
img_byte_arr = io.BytesIO()
save_format = ext.strip('.').upper()
if save_format == 'JPG': save_format = 'JPEG'
# Handle WEBP/JPEG quality if needed (using default for now)
img.save(img_byte_arr, format=save_format)
# Add to ZIP
zip_file.writestr(f"processed/{original_name}{ext}", img_byte_arr.getvalue())
except Exception as e:
print(f"Error processing {file.filename}: {e}")
# Optionally add an error log to the zip
zip_file.writestr(f"processed/errors/{file.filename}.txt", str(e))
zip_buffer.seek(0)
# Cleanup temp dir (we didn't actually write files to disk, just memory,
# but if we did, we'd clean here. The `img_byte_arr` approach avoids disk IO)
return send_file(
zip_buffer,
mimetype='application/zip',
as_attachment=True,
download_name=f'processed_images_{int(time.time())}.zip'
)
finally:
shutil.rmtree(temp_dir)
if __name__ == '__main__':
port = int(os.environ.get('PORT', 7860))
app.run(host='0.0.0.0', port=port)
|