print("=============================================") print("=== DÉMARRAGE DE MARKITDOWN WEBSERVER ===") print("=============================================") """ Serveur Flask pour MarkitDowne Local WebApp. Routes API : - GET / → Page principale - POST /upload → Upload et conversion d'un fichier - GET /history → Liste de l'historique - GET /download/ → Téléchargement du .md - POST /copy/ → Copie dans le presse-papier (retourne le texte) - POST /clear-history → Vide l'historique """ import os import uuid import socket from flask import ( Flask, render_template, request, jsonify, send_file, after_this_request, ) from converter import convert from history import add_entry, get_all, get_entry, clear app = Flask(__name__) # 1 Go max (augmenté pour supporter de gros dossiers) app.config["MAX_CONTENT_LENGTH"] = 1024 * 1024 * 1024 UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), "_uploads") os.makedirs(UPLOAD_FOLDER, exist_ok=True) # Changement du port pour 5001 afin d'éviter tout conflit avec le calculateur de piscine PORT = int(os.environ.get("PORT", 5001)) @app.route("/") def index(): """Page principale.""" return render_template("index.html") @app.route("/upload", methods=["POST"]) def upload(): """ Upload et conversion d'un ou plusieurs fichiers (ou dossiers). Retourne le Markdown généré pour chaque fichier. """ print("\n--- DEBUG: Received /upload request ---") files = request.files.getlist("files") paths = request.form.getlist("paths") print(f"DEBUG: Received {len(files)} files.") print(f"DEBUG: Received {len(paths)} paths: {paths}") if not files or not paths: print("DEBUG: No files or paths received. Aborting.") return jsonify({"success": False, "error": "Aucun fichier ou chemin reçu."}), 400 if len(files) != len(paths): print("DEBUG: Mismatch between number of files and paths. Aborting.") return jsonify({"success": False, "error": "Incohérence entre les fichiers et les chemins."}), 400 results = [] errors = [] for i, file in enumerate(files): filename = paths[i] print(f"\nDEBUG: Processing file #{i+1}: {filename}") if not filename: print("DEBUG: Skipping file with empty filename.") continue base_name = os.path.basename(filename.replace("\\", "/")) temp_filename = f"{uuid.uuid4().hex}_{base_name}" temp_path = os.path.join(UPLOAD_FOLDER, temp_filename) try: file.save(temp_path) print(f"DEBUG: Saved to temporary file: {temp_path}") markdown = convert(temp_path) print("DEBUG: Conversion successful.") entry = add_entry(filename, markdown) results.append({ "id": entry["id"], "filename": filename, "markdown": markdown, "size": entry["size"], "date": entry["date"], }) except Exception as e: print(f"DEBUG: An error occurred during conversion: {e}") errors.append({"filename": filename, "error": str(e)}) finally: if os.path.exists(temp_path): try: os.remove(temp_path) print(f"DEBUG: Removed temporary file: {temp_path}") except Exception as e: print(f"DEBUG: Failed to remove temporary file: {e}") print(f"--- DEBUG: Finished processing. Results: {len(results)}, Errors: {len(errors)} ---") return jsonify({ "success": True, "results": results, "errors": errors, }) @app.route("/history") def history(): """Retourne l'historique des conversions.""" return jsonify({"success": True, "data": get_all()}) @app.route("/download/") def download(entry_id): """Télécharge un fichier .md depuis l'historique.""" entry = get_entry(entry_id) if not entry: return jsonify({"success": False, "error": "Entrée introuvable."}), 404 original_filename = entry["filename"].replace("\\", "/") base = os.path.splitext(os.path.basename(original_filename))[0] md_filename = f"{base}.md" temp_md_filename = f"{uuid.uuid4().hex}_{md_filename}" md_path = os.path.join(UPLOAD_FOLDER, temp_md_filename) with open(md_path, "w", encoding="utf-8") as f: f.write(entry["markdown"]) @after_this_request def cleanup(response): if os.path.exists(md_path): try: os.remove(md_path) except Exception: pass return response return send_file( md_path, as_attachment=True, download_name=md_filename, mimetype="text/markdown", ) @app.route("/copy/") def copy(entry_id): """Retourne le contenu Markdown pour copie dans le presse-papier.""" entry = get_entry(entry_id) if not entry: return jsonify({"success": False, "error": "Entrée introuvable."}), 404 return jsonify({"success": True, "markdown": entry["markdown"]}) @app.route("/clear-history", methods=["POST"]) def clear_history(): """Vide l'historique.""" clear() return jsonify({"success": True}) # Ce bloc n'est exécuté que si on lance le script directement (ex: `python app.py`) # Il ne sera pas utilisé par Gunicorn sur Hugging Face. if __name__ == "__main__": with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: if s.connect_ex(('0.0.0.0', PORT)) == 0: print(f"ERREUR: Le port {PORT} est déjà utilisé.") print(" - Avez-vous une autre application (peut-être un autre projet) qui tourne ?") print(" - Essayez de l'arrêter et de relancer.") exit(1) print(f"\n MarkitDown WebApp lancée sur http://127.0.0.1:{PORT}\n") # On écoute sur 0.0.0.0 pour être compatible avec les conteneurs Docker app.run(host="0.0.0.0", port=PORT, debug=False)