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/') 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('/') 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/') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) @app.route("/api/pictures/", 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()