# plugins/media/routes.py import os from uuid import uuid4 from datetime import datetime from PIL import Image from flask import ( Blueprint, redirect, url_for, request, flash, send_from_directory, current_app, jsonify ) from flask_login import login_required, current_user from app import db from .models import Media, ImageHeart, FeaturedImage from plugins.plant.models import Plant bp = Blueprint("media", __name__, url_prefix="/media", template_folder="templates") # ----------------------------------------------------------------------------- # Make generate_image_url available in all templates # ----------------------------------------------------------------------------- @bp.app_context_processor def utility_processor(): def generate_image_url(path): if path: return url_for("media.media_file", filename=path) w, h = current_app.config.get("STANDARD_IMG_SIZE", (300, 200)) return f"https://placehold.co/{w}x{h}" return dict(generate_image_url=generate_image_url) # ----------------------------------------------------------------------------- # Helpers & config # ----------------------------------------------------------------------------- def allowed_file(filename): ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else "" return ext in current_app.config.get("ALLOWED_EXTENSIONS", {"png","jpg","jpeg","gif"}) def get_upload_path(): base = current_app.config.get("UPLOAD_FOLDER", "static/uploads") now = datetime.utcnow() subdir = os.path.join(str(now.year), f"{now.month:02}", f"{now.day:02}") full = os.path.join(base, subdir) os.makedirs(full, exist_ok=True) return full, subdir # ----------------------------------------------------------------------------- # Routes # ----------------------------------------------------------------------------- @bp.route("/", methods=["GET"]) def media_index(): return redirect(url_for("core_ui.home")) @bp.route("/files/", methods=["GET"]) def media_file(filename): # Strip leading "uploads/" if present if filename.startswith("uploads/"): filename = filename[len("uploads/"):] folder = current_app.config.get("UPLOAD_FOLDER", "static/uploads") return send_from_directory(folder, filename) @bp.route("/heart/", methods=["POST"]) @login_required def toggle_heart(media_id): existing = ImageHeart.query.filter_by(user_id=current_user.id, media_id=media_id).first() if existing: db.session.delete(existing) db.session.commit() return jsonify({"status": "unhearted"}) heart = ImageHeart(user_id=current_user.id, media_id=media_id) db.session.add(heart) db.session.commit() return jsonify({"status": "hearted"}) @bp.route("/add/", methods=["POST"]) @login_required def add_media(plant_uuid): plant = Plant.query.filter_by(uuid=plant_uuid).first_or_404() file = request.files.get("file") if not file or not allowed_file(file.filename): flash("Invalid or missing file.", "danger") return redirect(request.referrer or url_for("plant.edit", uuid_val=plant_uuid)) ext = file.filename.rsplit(".", 1)[-1].lower() filename = f"{uuid4()}.{ext}" full_path, subdir = get_upload_path() file.save(os.path.join(full_path, filename)) media = Media( file_url=os.path.join(subdir, filename).replace("\\", "/"), uploader_id=current_user.id, plant_id=plant.id ) db.session.add(media) db.session.commit() flash("Media uploaded successfully.", "success") return redirect(request.referrer or url_for("plant.edit", uuid_val=plant_uuid)) @bp.route("/feature/", methods=["POST"]) @login_required def set_featured_image(media_id): media = Media.query.get_or_404(media_id) if current_user.id != media.uploader_id and current_user.role != "admin": flash("Not authorized to set featured image.", "danger") return redirect(request.referrer or url_for("core_ui.home")) FeaturedImage.query.filter_by(media_id=media_id).delete() featured = FeaturedImage(media_id=media_id, is_featured=True) db.session.add(featured) db.session.commit() flash("Image set as featured.", "success") return redirect(request.referrer or url_for("plant.edit", uuid_val=media.plant.uuid)) @bp.route("/delete/", methods=["POST"]) @login_required def delete_media(media_id): media = Media.query.get_or_404(media_id) if current_user.id != media.uploader_id and current_user.role != "admin": flash("Not authorized to delete this media.", "danger") return redirect(request.referrer or url_for("core_ui.home")) full_path = os.path.join(current_app.config.get("UPLOAD_FOLDER", "static/uploads"), media.file_url) if os.path.exists(full_path): os.remove(full_path) db.session.delete(media) db.session.commit() flash("Media deleted.", "success") return redirect(request.referrer or url_for("plant.edit", uuid_val=media.plant.uuid)) @bp.route("/rotate/", methods=["POST"]) @login_required def rotate_media(media_id): media = Media.query.get_or_404(media_id) if current_user.id != media.uploader_id and current_user.role != "admin": flash("Not authorized to rotate this media.", "danger") return redirect(request.referrer or url_for("core_ui.home")) full_path = os.path.join(current_app.config.get("UPLOAD_FOLDER", "static/uploads"), media.file_url) try: with Image.open(full_path) as img: img.rotate(-90, expand=True).save(full_path) flash("Image rotated successfully.", "success") except Exception as e: flash(f"Failed to rotate image: {e}", "danger") return redirect(request.referrer or url_for("plant.edit", uuid_val=media.plant.uuid))