diff --git a/backend/services/converter.py b/backend/services/converter.py new file mode 100644 index 0000000..48071c0 --- /dev/null +++ b/backend/services/converter.py @@ -0,0 +1,75 @@ +import os +import json +import subprocess +from pathlib import Path +from fastapi import HTTPException +from config import EPT_DIR +from services.manifest import save_manifest +from utils.disk import get_entwine_path + +ENTWINE_PATH = get_entwine_path() +ENTWINE_AVAILABLE = ENTWINE_PATH is not None + +def run_entwine(input_path: Path, out_dir: Path) -> dict: + if not ENTWINE_AVAILABLE: + raise HTTPException(status_code=500, + detail="entwine n'est pas installé ou introuvable dans le PATH") + + out_dir.mkdir(parents=True, exist_ok=True) + + # entwine build -i -o + cmd = [ + ENTWINE_PATH, "build", + "-i", str(input_path.absolute()), + "-o", str(out_dir.absolute()), + ] + + print(f"Exécution: {' '.join(cmd)}") + print(f"CMD: {cmd}") + + proc = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + check=False, + timeout=7200, + env=os.environ.copy() + ) + + if proc.returncode != 0: + raise HTTPException(status_code=500, + detail=f"entwine failed (code {proc.returncode}):\n{proc.stdout}") + + result = _analyze_ept_output(out_dir, proc.stdout) + save_manifest(out_dir, result) + return result + + +def _analyze_ept_output(out_dir: Path, entwine_output: str) -> dict: + """ + Entwine produit un dossier EPT dont la structure est : + out_dir/ + ept.json ← fichier d'entrée principal + ept-data/ ← tuiles binaires + ept-hierarchy/ ← hiérarchie des noeuds + """ + ept_json = out_dir / "ept.json" + + result = { + "format": "ept", + "entry_file": None, + "entry_type": None, + "stdout": entwine_output[-2000:], + } + + if ept_json.exists(): + result["entry_file"] = ept_json.relative_to(EPT_DIR).as_posix() + result["entry_type"] = "ept.json" + # Le dossier EPT = le dossier contenant ept.json + result["ept_dir"] = str(ept_json.parent.relative_to(EPT_DIR)) + else: + raise HTTPException(status_code=500, + detail=f"entwine a terminé mais ept.json introuvable dans {out_dir}") + + return result \ No newline at end of file diff --git a/backend/services/html_generator.py b/backend/services/html_generator.py new file mode 100644 index 0000000..2924e0a --- /dev/null +++ b/backend/services/html_generator.py @@ -0,0 +1,87 @@ +from pathlib import Path +from typing import Optional +from config import EPT_DIR, POTREE_URL +import config + +def generate_viewer_html(pc_id: str, ept_dir: Optional[str], + embed: bool = False, potree_url: Optional[str] = None) -> str: + # Fallback : cherche ept.json si le manifest est absent + if not ept_dir: + out_dir = EPT_DIR / pc_id + ept_json = out_dir / "ept.json" + if ept_json.exists(): + ept_dir = pc_id + else: + return "

Erreur : ept.json introuvable pour cet ID

" + + height_style = "100vh" if embed else "800px" + base_url = "/static/potree" + potree_url = potree_url or config.POTREE_URL + + # L'URL vers ept.json servi via le montage statique /ept_data + ept_json_url = f"/ept_data/{ept_dir}/ept.json" + + return f""" + + + + + EPT Viewer - {pc_id} + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + +""" \ No newline at end of file diff --git a/backend/services/manifest.py b/backend/services/manifest.py new file mode 100644 index 0000000..a2e652d --- /dev/null +++ b/backend/services/manifest.py @@ -0,0 +1,24 @@ +import json +import time +from pathlib import Path + +def save_manifest(out_dir: Path, data: dict): + manifest = { + "conversion_time": time.time(), + "format": data.get("format", "ept"), # était "version" + "entry_file": data.get("entry_file"), + "entry_type": data.get("entry_type"), + "ept_dir": data.get("ept_dir"), + } + with open(out_dir / "manifest.json", 'w', encoding='utf-8') as f: + json.dump(manifest, f, indent=2) + +def read_manifest(out_dir: Path) -> dict: + manifest_file = out_dir / "manifest.json" + if not manifest_file.exists(): + return {} + try: + with open(manifest_file, 'r', encoding='utf-8') as f: + return json.load(f) + except: + return {} \ No newline at end of file