147 lines
5.1 KiB
Python
147 lines
5.1 KiB
Python
from datetime import datetime
|
|
import os
|
|
|
|
from flask import Flask, jsonify, request, send_from_directory, render_template
|
|
from flask_cors import CORS
|
|
from PIL import Image
|
|
from pillow_heif import register_heif_opener
|
|
from werkzeug.utils import secure_filename
|
|
|
|
from images import PetPicture
|
|
|
|
app = Flask(__name__, static_folder="ppq-frontend/dist/static", template_folder="ppq-frontend/dist")
|
|
app.config['UPLOAD_FOLDER'] = 'uploads'
|
|
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
|
|
|
CORS(app)
|
|
|
|
register_heif_opener()
|
|
|
|
|
|
def allowed_file(filename):
|
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'heic'}
|
|
return '.' in filename and \
|
|
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
|
|
|
def allowed_file(filename):
|
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'heic', 'heif'}
|
|
return '.' in filename and \
|
|
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
|
|
|
def is_heic_file(filename, mimetype):
|
|
"""Check if file is HEIC/HEIF"""
|
|
extension = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''
|
|
return extension in ['heic', 'heif'] or mimetype in ['image/heic', 'image/heif']
|
|
|
|
def convert_heic_image(file_stream, output_path, quality=85):
|
|
"""Convert HEIC image from file stream"""
|
|
try:
|
|
# Open image from stream
|
|
image = Image.open(file_stream)
|
|
|
|
# Convert to RGB if needed (HEIC can have different color modes)
|
|
if image.mode not in ['RGB', 'L']:
|
|
image = image.convert('RGB')
|
|
|
|
# Save as JPEG
|
|
image.save(output_path, 'JPEG', quality=quality, optimize=True)
|
|
return True, None
|
|
except Exception as e:
|
|
return False, str(e)
|
|
|
|
|
|
# Serve React static files
|
|
@app.route('/static/<path:filename>')
|
|
def static_files(filename):
|
|
return send_from_directory(app.static_folder, filename)
|
|
|
|
# Serve the React app for all routes (catch-all)
|
|
@app.route('/', defaults={'path': ''})
|
|
@app.route('/<path:path>')
|
|
def serve_react_app(path):
|
|
if path and os.path.exists(os.path.join(app.template_folder, path)):
|
|
return send_from_directory(app.template_folder, path)
|
|
return render_template('index.html')
|
|
|
|
# List pictures
|
|
# Create a picture entry
|
|
# Edit a picture metadata
|
|
@app.route("/api/pictures", methods=["GET", "POST", "PATCH"])
|
|
def list_pictures():
|
|
if request.method == "GET":
|
|
return jsonify([pp.get_json() for pp in PetPicture.get_all()])
|
|
elif request.method == "POST":
|
|
title = request.form.get("title")
|
|
contributor = request.form.get("contributor")
|
|
description = request.form.get("description")
|
|
image = request.files.get("image")
|
|
timestamp = int(datetime.now().timestamp())
|
|
|
|
# Validate
|
|
if image is None:
|
|
return jsonify({"error": "No file provided"}), 400
|
|
|
|
if image and allowed_file(image.filename):
|
|
# Check if it's HEIC
|
|
if is_heic_file(image.filename, image.mimetype):
|
|
# Convert HEIC to JPEG
|
|
original_name = image.filename.rsplit('.', 1)[0]
|
|
filename = f"{timestamp}_{original_name}.jpg"
|
|
output_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
|
|
|
# Reset file stream position
|
|
image.stream.seek(0)
|
|
|
|
success, error_msg = convert_heic_image(image.stream, output_path)
|
|
|
|
if not success:
|
|
return jsonify({'error': f'HEIC conversion failed: {error_msg}'}), 500
|
|
|
|
else:
|
|
# Secure the filename
|
|
filename = secure_filename(image.filename)
|
|
|
|
# Save the file
|
|
image.save(os.path.join('uploads', filename))
|
|
|
|
pic = PetPicture(title=title,
|
|
contributor=contributor,
|
|
created_at=timestamp,
|
|
filepath=filename,
|
|
description=description)
|
|
pic.upsert()
|
|
|
|
return jsonify({'message': 'File uploaded successfully', 'filename': filename})
|
|
|
|
return jsonify({'error': 'Something went wrong'}), 400
|
|
|
|
|
|
@app.route('/uploads/<filename>')
|
|
def uploaded_file(filename):
|
|
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
|
|
|
|
@app.route("/api/pictures/<string:uuid>", methods=["GET", "PATCH"])
|
|
def single_picture(uuid):
|
|
if request.method == "GET":
|
|
pet_picture = PetPicture.get(uuid=uuid)
|
|
return jsonify(pet_picture.get_json())
|
|
elif request.method == "PATCH":
|
|
data = request.get_json()
|
|
print(data)
|
|
pic = PetPicture(uuid=uuid,
|
|
title=data.get("title"),
|
|
contributor=data.get("contributor"),
|
|
created_at=data.get("created_at"),
|
|
filepath=data.get("filepath"),
|
|
description=data.get("description"))
|
|
pic.upsert()
|
|
return jsonify(pic.get_json()), 200
|
|
|
|
|
|
def main():
|
|
print("Hello from ppq!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|