chore: CLAUDE.md compliance — relative paths, CORS, README tech stack, .vch-description

- Fix absolute API paths in index.html (/analyze, /move, /preview → relative)
- Allow all CORS origins in server.py for reverse-proxy compatibility
- Add tech stack section to README.md
- Create .vch-description for VCH Showcase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 14:13:43 +00:00
parent a90c542d9f
commit 9f44b8c4f2
4 changed files with 14 additions and 5 deletions

1
.vch-description Normal file
View File

@@ -0,0 +1 @@
Foto-Kurator hilft Fotografen dabei, schlechte Fotos aus einem Shooting schnell auszusortieren. Die App analysiert einen Ordner automatisch auf unscharfe, über- oder unterbelichtete Fotos sowie Duplikate — optional auch mit KI. Bevor Fotos verschoben werden, zeigt die App eine Übersicht zur manuellen Kontrolle an.

View File

@@ -30,3 +30,11 @@ Der Browser öffnet automatisch http://localhost:8000.
- **KI-Analyse** — Claude Vision API (optional, ca. 0,003 € / Foto) - **KI-Analyse** — Claude Vision API (optional, ca. 0,003 € / Foto)
Aussortierte Fotos landen in `_aussortiert/` im analysierten Ordner. Aussortierte Fotos landen in `_aussortiert/` im analysierten Ordner.
## Tech Stack
- **Backend:** Python 3, FastAPI, Uvicorn
- **Bildanalyse:** OpenCV (Laplacian Variance), Pillow, ImageHash (pHash/MD5)
- **KI-Analyse (optional):** Anthropic Claude Vision API
- **Frontend:** Vanilla HTML/CSS/JavaScript (kein Framework)
- **Konfiguration:** python-dotenv

View File

@@ -218,7 +218,7 @@
// --- Folder picker --- // --- Folder picker ---
el("pick-btn").addEventListener("click", async () => { el("pick-btn").addEventListener("click", async () => {
try { try {
const res = await fetch("/pick-folder"); const res = await fetch("pick-folder");
if (res.status === 204) return; if (res.status === 204) return;
const data = await res.json(); const data = await res.json();
el("folder-input").value = data.folder; el("folder-input").value = data.folder;
@@ -250,7 +250,7 @@
let data; let data;
try { try {
const res = await fetch("/analyze", { const res = await fetch("analyze", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload), body: JSON.stringify(payload),
@@ -285,7 +285,7 @@
// --- Review --- // --- Review ---
function makeThumb(path, name) { function makeThumb(path, name) {
const img = document.createElement("img"); const img = document.createElement("img");
img.src = "/preview?path=" + encodeURIComponent(path); img.src = "preview?path=" + encodeURIComponent(path);
img.alt = name; img.alt = name;
img.onerror = function() { this.style.display = "none"; }; img.onerror = function() { this.style.display = "none"; };
img.addEventListener("click", () => openLightbox(img.src, name)); img.addEventListener("click", () => openLightbox(img.src, name));
@@ -380,7 +380,7 @@
el("progress-label").textContent = "Verschiebe " + toMove.length + " Fotos..."; el("progress-label").textContent = "Verschiebe " + toMove.length + " Fotos...";
try { try {
const res = await fetch("/move", { const res = await fetch("move", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ paths: toMove, folder: currentFolder }), body: JSON.stringify({ paths: toMove, folder: currentFolder }),

View File

@@ -19,7 +19,7 @@ app = FastAPI(title="Foto-Kurator")
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=["http://localhost:8000"], allow_origins=["*"],
allow_methods=["GET", "POST"], allow_methods=["GET", "POST"],
allow_headers=["Content-Type"], allow_headers=["Content-Type"],
) )