diff --git a/backend/main.py b/backend/main.py index 35176ba..1a0a1dd 100644 --- a/backend/main.py +++ b/backend/main.py @@ -172,6 +172,15 @@ async def api_fonds(): return lister_fonds() +@app.delete("/api/fonds/{nom}") +async def api_supprimer_fond(nom: str): + chemin = DOSSIER_FONDS / nom + if chemin.exists() and chemin.parent == DOSSIER_FONDS: + chemin.unlink() + return {"succes": True} + return JSONResponse({"erreur": "Fichier introuvable"}, status_code=404) + + # --- API Collage --- @app.post("/api/strip") diff --git a/frontend/css/style.css b/frontend/css/style.css index 267fae2..49c121c 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -294,6 +294,43 @@ html, body { box-shadow: var(--ombre); } +.barre-fonds { + display: flex; + gap: 0.8rem; + padding: 0.8rem; + overflow-x: auto; + width: 100%; + justify-content: center; +} + +.btn-fond { + width: 80px; + height: 60px; + border-radius: 8px; + border: 3px solid transparent; + cursor: pointer; + transition: var(--transition); + overflow: hidden; + flex-shrink: 0; + padding: 0; + background: var(--fond-carte); +} + +.btn-fond img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.btn-fond.actif { + border-color: var(--primaire); + box-shadow: 0 0 12px rgba(233, 30, 99, 0.5); +} + +.btn-fond:active { + transform: scale(0.93); +} + .barre-filtres, .barre-overlays { display: flex; gap: 0.8rem; @@ -828,6 +865,44 @@ h3 { border-radius: 50%; } +/* Liste fonds admin */ +.liste-fonds { + display: flex; + flex-wrap: wrap; + gap: 0.8rem; +} + +.fond-item { + position: relative; + width: 120px; + height: 80px; + border-radius: 8px; + overflow: hidden; + border: 2px solid #333; +} + +.fond-item img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.fond-item .btn-supprimer { + position: absolute; + top: 2px; right: 2px; + background: rgba(0,0,0,0.6); + border: none; + color: var(--danger); + font-size: 1rem; + cursor: pointer; + border-radius: 50%; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + /* Liste cadres */ .liste-cadres { display: flex; diff --git a/frontend/index.html b/frontend/index.html index 6c57f4e..9e2581c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -67,6 +67,8 @@
+Importez des images de fond. Les utilisateurs choisiront leur fond apres la photo.
+Aucun fond importe
+Aucun fond importe
'; + return; + } + + for (const nom of fonds) { + const div = document.createElement('div'); + div.className = 'fond-item'; + + const img = document.createElement('img'); + img.src = `/assets/backgrounds/${nom}`; + img.alt = nom; + + const btnSuppr = document.createElement('button'); + btnSuppr.className = 'btn-supprimer'; + btnSuppr.innerHTML = '✕'; + btnSuppr.onclick = () => supprimerFond(nom); + + div.appendChild(img); + div.appendChild(btnSuppr); + liste.appendChild(div); + } +} + +async function uploaderFond() { + const input = document.getElementById('input-fond'); + if (!input.files.length) return; + + const formData = new FormData(); + formData.append('fichier', input.files[0]); + await fetch('/api/upload/fond', { method: 'POST', body: formData }); + input.value = ''; + chargerFondsAdmin(); + afficherStatut('Fond importe', 'succes'); +} + +async function supprimerFond(nom) { + if (!confirm(`Supprimer le fond "${nom}" ?`)) return; + await fetch(`/api/fonds/${encodeURIComponent(nom)}`, { method: 'DELETE' }); + chargerFondsAdmin(); +} + // === ANIMATIONS === const EMOJI_MAP = { 3: '🤪', 2: '😱', 1: '🔥' }; diff --git a/frontend/js/camera.js b/frontend/js/camera.js index ee703eb..ee713dd 100644 --- a/frontend/js/camera.js +++ b/frontend/js/camera.js @@ -208,6 +208,9 @@ function afficherPreviewPhoto(chemin) { if (config.fonctionnalites?.overlays) { chargerOverlays(); } + if (config.fonctionnalites?.chroma_key) { + chargerFonds(); + } } // --- Compteur accueil --- diff --git a/frontend/js/effects.js b/frontend/js/effects.js index b7335a1..07fbb2b 100644 --- a/frontend/js/effects.js +++ b/frontend/js/effects.js @@ -1,7 +1,8 @@ -/* Module effets - Filtres et overlays */ +/* Module effets - Filtres, overlays et fond vert */ let filtreActuel = 'original'; let overlayActuel = null; +let fondActuel = null; async function chargerFiltres() { const filtres = await apiGet('/api/filtres'); @@ -18,7 +19,6 @@ async function chargerFiltres() { } async function appliquerFiltreUI(filtre, btn) { - // Activer visuellement document.querySelectorAll('.btn-filtre').forEach(b => b.classList.remove('actif')); btn.classList.add('actif'); filtreActuel = filtre; @@ -55,7 +55,6 @@ async function chargerOverlays() { barre.classList.remove('cache'); barre.innerHTML = ''; - // Bouton "sans overlay" const btnAucun = document.createElement('button'); btnAucun.className = 'btn-overlay actif'; btnAucun.textContent = 'Sans cadre'; @@ -91,7 +90,71 @@ function retirerOverlay(btn) { btn.classList.add('actif'); overlayActuel = null; - // Revenir a la photo avec filtre actuel (ou originale) + if (filtreActuel !== 'original') { + appliquerFiltreUI(filtreActuel, document.querySelector('.btn-filtre.actif')); + } else { + const chemin = '/data/photos/' + photosSession[0]; + document.getElementById('photo-resultat').src = chemin; + document.getElementById('photo-partage').src = chemin; + photoFinale = photosSession[0]; + } +} + +// === Fond vert (chroma key) === + +async function chargerFonds() { + const fonds = await apiGet('/api/fonds'); + const barre = document.getElementById('barre-fonds'); + + if (!config.fonctionnalites?.chroma_key || fonds.length === 0) { + barre.classList.add('cache'); + return; + } + + barre.classList.remove('cache'); + barre.innerHTML = ''; + fondActuel = null; + + // Bouton "original" (pas de chroma key) + const btnOriginal = document.createElement('button'); + btnOriginal.className = 'btn-fond actif'; + btnOriginal.innerHTML = 'Original'; + btnOriginal.addEventListener('click', () => retirerFond(btnOriginal)); + barre.appendChild(btnOriginal); + + for (const nom of fonds) { + const btn = document.createElement('button'); + btn.className = 'btn-fond'; + const img = document.createElement('img'); + img.src = `/assets/backgrounds/${nom}`; + img.alt = nom; + btn.appendChild(img); + btn.addEventListener('click', () => appliquerFondUI(nom, btn)); + barre.appendChild(btn); + } +} + +async function appliquerFondUI(nom, btn) { + document.querySelectorAll('.btn-fond').forEach(b => b.classList.remove('actif')); + btn.classList.add('actif'); + fondActuel = nom; + + const photo = photosSession[0]; + const resultat = await apiPost('/api/chroma', { photo, fond: nom }); + + if (resultat.nom) { + photoFinale = resultat.nom; + document.getElementById('photo-resultat').src = '/data/exports/' + resultat.nom; + document.getElementById('photo-partage').src = '/data/exports/' + resultat.nom; + } +} + +function retirerFond(btn) { + document.querySelectorAll('.btn-fond').forEach(b => b.classList.remove('actif')); + btn.classList.add('actif'); + fondActuel = null; + + // Revenir a la photo originale ou avec filtre if (filtreActuel !== 'original') { appliquerFiltreUI(filtreActuel, document.querySelector('.btn-filtre.actif')); } else {