Markdown / app.py
GauthierDemonchy
Mise à jour: passage à Python 3.11 pour compatibilité avec markitdown
7ece4a1
Raw
History Blame Contribute Delete
6.12 kB
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/<id> → Téléchargement du .md
- POST /copy/<id> → 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/<entry_id>")
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/<entry_id>")
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)